@@ -241,6 +241,7 @@ static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
241
241
static void RelationClearRelation (Relation relation , bool rebuild );
242
242
243
243
static void RelationReloadIndexInfo (Relation relation );
244
+ static void RelationReloadNailed (Relation relation );
244
245
static void RelationFlushRelation (Relation relation );
245
246
static void RememberToFreeTupleDescAtEOX (TupleDesc td );
246
247
static void AtEOXact_cleanup (Relation relation , bool isCommit );
@@ -277,7 +278,7 @@ static void IndexSupportInitialize(oidvector *indclass,
277
278
static OpClassCacheEnt * LookupOpclassInfo (Oid operatorClassOid ,
278
279
StrategyNumber numSupport );
279
280
static void RelationCacheInitFileRemoveInDir (const char * tblspcpath );
280
- static void unlink_initfile (const char * initfilename );
281
+ static void unlink_initfile (const char * initfilename , int elevel );
281
282
282
283
283
284
/*
@@ -1782,7 +1783,16 @@ RelationIdGetRelation(Oid relationId)
1782
1783
RelationReloadIndexInfo (rd );
1783
1784
else
1784
1785
RelationClearRelation (rd , true);
1785
- Assert (rd -> rd_isvalid );
1786
+
1787
+ /*
1788
+ * Normally entries need to be valid here, but before the relcache
1789
+ * has been initialized, not enough infrastructure exists to
1790
+ * perform pg_class lookups. The structure of such entries doesn't
1791
+ * change, but we still want to update the rd_rel entry. So
1792
+ * rd_isvalid = false is left in place for a later lookup.
1793
+ */
1794
+ Assert (rd -> rd_isvalid ||
1795
+ (rd -> rd_isnailed && !criticalRelcachesBuilt ));
1786
1796
}
1787
1797
return rd ;
1788
1798
}
@@ -1985,6 +1995,81 @@ RelationReloadIndexInfo(Relation relation)
1985
1995
relation -> rd_isvalid = true;
1986
1996
}
1987
1997
1998
+ /*
1999
+ * RelationReloadNailed - reload minimal information for nailed relations.
2000
+ *
2001
+ * The structure of a nailed relation can never change (which is good, because
2002
+ * we rely on knowing their structure to be able to read catalog content). But
2003
+ * some parts, e.g. pg_class.relfrozenxid, are still important to have
2004
+ * accurate content for. Therefore those need to be reloaded after the arrival
2005
+ * of invalidations.
2006
+ */
2007
+ static void
2008
+ RelationReloadNailed (Relation relation )
2009
+ {
2010
+ Assert (relation -> rd_isnailed );
2011
+
2012
+ /*
2013
+ * Redo RelationInitPhysicalAddr in case it is a mapped relation whose
2014
+ * mapping changed.
2015
+ */
2016
+ RelationInitPhysicalAddr (relation );
2017
+
2018
+ /* flag as needing to be revalidated */
2019
+ relation -> rd_isvalid = false;
2020
+
2021
+ /*
2022
+ * Can only reread catalog contents if in a transaction. If the relation
2023
+ * is currently open (not counting the nailed refcount), do so
2024
+ * immediately. Otherwise we've already marked the entry as possibly
2025
+ * invalid, and it'll be fixed when next opened.
2026
+ */
2027
+ if (!IsTransactionState () || relation -> rd_refcnt <= 1 )
2028
+ return ;
2029
+
2030
+ if (relation -> rd_rel -> relkind == RELKIND_INDEX )
2031
+ {
2032
+ /*
2033
+ * If it's a nailed-but-not-mapped index, then we need to re-read the
2034
+ * pg_class row to see if its relfilenode changed.
2035
+ */
2036
+ RelationReloadIndexInfo (relation );
2037
+ }
2038
+ else
2039
+ {
2040
+ /*
2041
+ * Reload a non-index entry. We can't easily do so if relcaches
2042
+ * aren't yet built, but that's fine because at that stage the
2043
+ * attributes that need to be current (like relfrozenxid) aren't yet
2044
+ * accessed. To ensure the entry will later be revalidated, we leave
2045
+ * it in invalid state, but allow use (cf. RelationIdGetRelation()).
2046
+ */
2047
+ if (criticalRelcachesBuilt )
2048
+ {
2049
+ HeapTuple pg_class_tuple ;
2050
+ Form_pg_class relp ;
2051
+
2052
+ /*
2053
+ * NB: Mark the entry as valid before starting to scan, to avoid
2054
+ * self-recursion when re-building pg_class.
2055
+ */
2056
+ relation -> rd_isvalid = true;
2057
+
2058
+ pg_class_tuple = ScanPgRelation (RelationGetRelid (relation ),
2059
+ true, false);
2060
+ relp = (Form_pg_class ) GETSTRUCT (pg_class_tuple );
2061
+ memcpy (relation -> rd_rel , relp , CLASS_TUPLE_SIZE );
2062
+ heap_freetuple (pg_class_tuple );
2063
+
2064
+ /*
2065
+ * Again mark as valid, to protect against concurrently arriving
2066
+ * invalidations.
2067
+ */
2068
+ relation -> rd_isvalid = true;
2069
+ }
2070
+ }
2071
+ }
2072
+
1988
2073
/*
1989
2074
* RelationDestroyRelation
1990
2075
*
@@ -2089,26 +2174,12 @@ RelationClearRelation(Relation relation, bool rebuild)
2089
2174
RelationCloseSmgr (relation );
2090
2175
2091
2176
/*
2092
- * Never, never ever blow away a nailed-in system relation, because we'd
2093
- * be unable to recover. However, we must redo RelationInitPhysicalAddr
2094
- * in case it is a mapped relation whose mapping changed.
2095
- *
2096
- * If it's a nailed-but-not-mapped index, then we need to re-read the
2097
- * pg_class row to see if its relfilenode changed. We do that immediately
2098
- * if we're inside a valid transaction and the relation is open (not
2099
- * counting the nailed refcount). Otherwise just mark the entry as
2100
- * possibly invalid, and it'll be fixed when next opened.
2177
+ * Treat nailed-in system relations separately, they always need to be
2178
+ * accessible, so we can't blow them away.
2101
2179
*/
2102
2180
if (relation -> rd_isnailed )
2103
2181
{
2104
- RelationInitPhysicalAddr (relation );
2105
-
2106
- if (relation -> rd_rel -> relkind == RELKIND_INDEX )
2107
- {
2108
- relation -> rd_isvalid = false; /* needs to be revalidated */
2109
- if (relation -> rd_refcnt > 1 && IsTransactionState ())
2110
- RelationReloadIndexInfo (relation );
2111
- }
2182
+ RelationReloadNailed (relation );
2112
2183
return ;
2113
2184
}
2114
2185
@@ -5380,24 +5451,26 @@ write_item(const void *data, Size len, FILE *fp)
5380
5451
5381
5452
/*
5382
5453
* Determine whether a given relation (identified by OID) is one of the ones
5383
- * we should store in the local relcache init file.
5454
+ * we should store in a relcache init file.
5384
5455
*
5385
5456
* We must cache all nailed rels, and for efficiency we should cache every rel
5386
5457
* that supports a syscache. The former set is almost but not quite a subset
5387
- * of the latter. Currently, we must special-case TriggerRelidNameIndexId,
5388
- * which RelationCacheInitializePhase3 chooses to nail for efficiency reasons,
5389
- * but which does not support any syscache.
5390
- *
5391
- * Note: this function is currently never called for shared rels. If it were,
5392
- * we'd probably also need a special case for DatabaseNameIndexId, which is
5393
- * critical but does not support a syscache.
5458
+ * of the latter. The special cases are relations where
5459
+ * RelationCacheInitializePhase2/3 chooses to nail for efficiency reasons, but
5460
+ * which do not support any syscache.
5394
5461
*/
5395
5462
bool
5396
5463
RelationIdIsInInitFile (Oid relationId )
5397
5464
{
5398
- if (relationId == TriggerRelidNameIndexId )
5465
+ if (relationId == SharedSecLabelRelationId ||
5466
+ relationId == TriggerRelidNameIndexId ||
5467
+ relationId == DatabaseNameIndexId ||
5468
+ relationId == SharedSecLabelObjectIndexId )
5399
5469
{
5400
- /* If this Assert fails, we don't need this special case anymore. */
5470
+ /*
5471
+ * If this Assert fails, we don't need the applicable special case
5472
+ * anymore.
5473
+ */
5401
5474
Assert (!RelationSupportsSysCache (relationId ));
5402
5475
return true;
5403
5476
}
@@ -5471,38 +5544,30 @@ RelationHasUnloggedIndex(Relation rel)
5471
5544
* We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
5472
5545
* then release the lock in RelationCacheInitFilePostInvalidate. Caller must
5473
5546
* send any pending SI messages between those calls.
5474
- *
5475
- * Notice this deals only with the local init file, not the shared init file.
5476
- * The reason is that there can never be a "significant" change to the
5477
- * relcache entry of a shared relation; the most that could happen is
5478
- * updates of noncritical fields such as relpages/reltuples. So, while
5479
- * it's worth updating the shared init file from time to time, it can never
5480
- * be invalid enough to make it necessary to remove it.
5481
5547
*/
5482
5548
void
5483
5549
RelationCacheInitFilePreInvalidate (void )
5484
5550
{
5485
- char initfilename [MAXPGPATH ];
5551
+ char localinitfname [MAXPGPATH ];
5552
+ char sharedinitfname [MAXPGPATH ];
5486
5553
5487
- snprintf (initfilename , sizeof (initfilename ), "%s/%s" ,
5488
- DatabasePath , RELCACHE_INIT_FILENAME );
5554
+ if (DatabasePath )
5555
+ snprintf (localinitfname , sizeof (localinitfname ), "%s/%s" ,
5556
+ DatabasePath , RELCACHE_INIT_FILENAME );
5557
+ snprintf (sharedinitfname , sizeof (sharedinitfname ), "global/%s" ,
5558
+ RELCACHE_INIT_FILENAME );
5489
5559
5490
5560
LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
5491
5561
5492
- if (unlink (initfilename ) < 0 )
5493
- {
5494
- /*
5495
- * The file might not be there if no backend has been started since
5496
- * the last removal. But complain about failures other than ENOENT.
5497
- * Fortunately, it's not too late to abort the transaction if we can't
5498
- * get rid of the would-be-obsolete init file.
5499
- */
5500
- if (errno != ENOENT )
5501
- ereport (ERROR ,
5502
- (errcode_for_file_access (),
5503
- errmsg ("could not remove cache file \"%s\": %m" ,
5504
- initfilename )));
5505
- }
5562
+ /*
5563
+ * The files might not be there if no backend has been started since the
5564
+ * last removal. But complain about failures other than ENOENT with
5565
+ * ERROR. Fortunately, it's not too late to abort the transaction if we
5566
+ * can't get rid of the would-be-obsolete init file.
5567
+ */
5568
+ if (DatabasePath )
5569
+ unlink_initfile (localinitfname , ERROR );
5570
+ unlink_initfile (sharedinitfname , ERROR );
5506
5571
}
5507
5572
5508
5573
void
@@ -5528,13 +5593,9 @@ RelationCacheInitFileRemove(void)
5528
5593
struct dirent * de ;
5529
5594
char path [MAXPGPATH + 10 + sizeof (TABLESPACE_VERSION_DIRECTORY )];
5530
5595
5531
- /*
5532
- * We zap the shared cache file too. In theory it can't get out of sync
5533
- * enough to be a problem, but in data-corruption cases, who knows ...
5534
- */
5535
5596
snprintf (path , sizeof (path ), "global/%s" ,
5536
5597
RELCACHE_INIT_FILENAME );
5537
- unlink_initfile (path );
5598
+ unlink_initfile (path , LOG );
5538
5599
5539
5600
/* Scan everything in the default tablespace */
5540
5601
RelationCacheInitFileRemoveInDir ("base" );
@@ -5586,20 +5647,23 @@ RelationCacheInitFileRemoveInDir(const char *tblspcpath)
5586
5647
/* Try to remove the init file in each database */
5587
5648
snprintf (initfilename , sizeof (initfilename ), "%s/%s/%s" ,
5588
5649
tblspcpath , de -> d_name , RELCACHE_INIT_FILENAME );
5589
- unlink_initfile (initfilename );
5650
+ unlink_initfile (initfilename , LOG );
5590
5651
}
5591
5652
}
5592
5653
5593
5654
FreeDir (dir );
5594
5655
}
5595
5656
5596
5657
static void
5597
- unlink_initfile (const char * initfilename )
5658
+ unlink_initfile (const char * initfilename , int elevel )
5598
5659
{
5599
5660
if (unlink (initfilename ) < 0 )
5600
5661
{
5601
5662
/* It might not be there, but log any error other than ENOENT */
5602
5663
if (errno != ENOENT )
5603
- elog (LOG , "could not remove cache file \"%s\": %m" , initfilename );
5664
+ ereport (elevel ,
5665
+ (errcode_for_file_access (),
5666
+ errmsg ("could not remove cache file \"%s\": %m" ,
5667
+ initfilename )));
5604
5668
}
5605
5669
}
0 commit comments