@@ -236,6 +236,7 @@ static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
236
236
static void RelationClearRelation (Relation relation , bool rebuild );
237
237
238
238
static void RelationReloadIndexInfo (Relation relation );
239
+ static void RelationReloadNailed (Relation relation );
239
240
static void RelationFlushRelation (Relation relation );
240
241
static void RememberToFreeTupleDescAtEOX (TupleDesc td );
241
242
static void AtEOXact_cleanup (Relation relation , bool isCommit );
@@ -270,7 +271,7 @@ static void IndexSupportInitialize(oidvector *indclass,
270
271
static OpClassCacheEnt * LookupOpclassInfo (Oid operatorClassOid ,
271
272
StrategyNumber numSupport );
272
273
static void RelationCacheInitFileRemoveInDir (const char * tblspcpath );
273
- static void unlink_initfile (const char * initfilename );
274
+ static void unlink_initfile (const char * initfilename , int elevel );
274
275
275
276
276
277
/*
@@ -1669,7 +1670,16 @@ RelationIdGetRelation(Oid relationId)
1669
1670
RelationReloadIndexInfo (rd );
1670
1671
else
1671
1672
RelationClearRelation (rd , true);
1672
- Assert (rd -> rd_isvalid );
1673
+
1674
+ /*
1675
+ * Normally entries need to be valid here, but before the relcache
1676
+ * has been initialized, not enough infrastructure exists to
1677
+ * perform pg_class lookups. The structure of such entries doesn't
1678
+ * change, but we still want to update the rd_rel entry. So
1679
+ * rd_isvalid = false is left in place for a later lookup.
1680
+ */
1681
+ Assert (rd -> rd_isvalid ||
1682
+ (rd -> rd_isnailed && !criticalRelcachesBuilt ));
1673
1683
}
1674
1684
return rd ;
1675
1685
}
@@ -1872,6 +1882,81 @@ RelationReloadIndexInfo(Relation relation)
1872
1882
relation -> rd_isvalid = true;
1873
1883
}
1874
1884
1885
+ /*
1886
+ * RelationReloadNailed - reload minimal information for nailed relations.
1887
+ *
1888
+ * The structure of a nailed relation can never change (which is good, because
1889
+ * we rely on knowing their structure to be able to read catalog content). But
1890
+ * some parts, e.g. pg_class.relfrozenxid, are still important to have
1891
+ * accurate content for. Therefore those need to be reloaded after the arrival
1892
+ * of invalidations.
1893
+ */
1894
+ static void
1895
+ RelationReloadNailed (Relation relation )
1896
+ {
1897
+ Assert (relation -> rd_isnailed );
1898
+
1899
+ /*
1900
+ * Redo RelationInitPhysicalAddr in case it is a mapped relation whose
1901
+ * mapping changed.
1902
+ */
1903
+ RelationInitPhysicalAddr (relation );
1904
+
1905
+ /* flag as needing to be revalidated */
1906
+ relation -> rd_isvalid = false;
1907
+
1908
+ /*
1909
+ * Can only reread catalog contents if in a transaction. If the relation
1910
+ * is currently open (not counting the nailed refcount), do so
1911
+ * immediately. Otherwise we've already marked the entry as possibly
1912
+ * invalid, and it'll be fixed when next opened.
1913
+ */
1914
+ if (!IsTransactionState () || relation -> rd_refcnt <= 1 )
1915
+ return ;
1916
+
1917
+ if (relation -> rd_rel -> relkind == RELKIND_INDEX )
1918
+ {
1919
+ /*
1920
+ * If it's a nailed-but-not-mapped index, then we need to re-read the
1921
+ * pg_class row to see if its relfilenode changed.
1922
+ */
1923
+ RelationReloadIndexInfo (relation );
1924
+ }
1925
+ else
1926
+ {
1927
+ /*
1928
+ * Reload a non-index entry. We can't easily do so if relcaches
1929
+ * aren't yet built, but that's fine because at that stage the
1930
+ * attributes that need to be current (like relfrozenxid) aren't yet
1931
+ * accessed. To ensure the entry will later be revalidated, we leave
1932
+ * it in invalid state, but allow use (cf. RelationIdGetRelation()).
1933
+ */
1934
+ if (criticalRelcachesBuilt )
1935
+ {
1936
+ HeapTuple pg_class_tuple ;
1937
+ Form_pg_class relp ;
1938
+
1939
+ /*
1940
+ * NB: Mark the entry as valid before starting to scan, to avoid
1941
+ * self-recursion when re-building pg_class.
1942
+ */
1943
+ relation -> rd_isvalid = true;
1944
+
1945
+ pg_class_tuple = ScanPgRelation (RelationGetRelid (relation ),
1946
+ true, false);
1947
+ relp = (Form_pg_class ) GETSTRUCT (pg_class_tuple );
1948
+ memcpy (relation -> rd_rel , relp , CLASS_TUPLE_SIZE );
1949
+ heap_freetuple (pg_class_tuple );
1950
+
1951
+ /*
1952
+ * Again mark as valid, to protect against concurrently arriving
1953
+ * invalidations.
1954
+ */
1955
+ relation -> rd_isvalid = true;
1956
+ }
1957
+ }
1958
+ }
1959
+
1875
1960
/*
1876
1961
* RelationDestroyRelation
1877
1962
*
@@ -1975,26 +2060,12 @@ RelationClearRelation(Relation relation, bool rebuild)
1975
2060
RelationCloseSmgr (relation );
1976
2061
1977
2062
/*
1978
- * Never, never ever blow away a nailed-in system relation, because we'd
1979
- * be unable to recover. However, we must redo RelationInitPhysicalAddr
1980
- * in case it is a mapped relation whose mapping changed.
1981
- *
1982
- * If it's a nailed-but-not-mapped index, then we need to re-read the
1983
- * pg_class row to see if its relfilenode changed. We do that immediately
1984
- * if we're inside a valid transaction and the relation is open (not
1985
- * counting the nailed refcount). Otherwise just mark the entry as
1986
- * possibly invalid, and it'll be fixed when next opened.
2063
+ * Treat nailed-in system relations separately, they always need to be
2064
+ * accessible, so we can't blow them away.
1987
2065
*/
1988
2066
if (relation -> rd_isnailed )
1989
2067
{
1990
- RelationInitPhysicalAddr (relation );
1991
-
1992
- if (relation -> rd_rel -> relkind == RELKIND_INDEX )
1993
- {
1994
- relation -> rd_isvalid = false; /* needs to be revalidated */
1995
- if (relation -> rd_refcnt > 1 && IsTransactionState ())
1996
- RelationReloadIndexInfo (relation );
1997
- }
2068
+ RelationReloadNailed (relation );
1998
2069
return ;
1999
2070
}
2000
2071
@@ -5061,24 +5132,24 @@ write_item(const void *data, Size len, FILE *fp)
5061
5132
5062
5133
/*
5063
5134
* Determine whether a given relation (identified by OID) is one of the ones
5064
- * we should store in the local relcache init file.
5135
+ * we should store in a relcache init file.
5065
5136
*
5066
5137
* We must cache all nailed rels, and for efficiency we should cache every rel
5067
5138
* that supports a syscache. The former set is almost but not quite a subset
5068
- * of the latter. Currently, we must special-case TriggerRelidNameIndexId,
5069
- * which RelationCacheInitializePhase3 chooses to nail for efficiency reasons,
5070
- * but which does not support any syscache.
5071
- *
5072
- * Note: this function is currently never called for shared rels. If it were,
5073
- * we'd probably also need a special case for DatabaseNameIndexId, which is
5074
- * critical but does not support a syscache.
5139
+ * of the latter. The special cases are relations where
5140
+ * RelationCacheInitializePhase2/3 chooses to nail for efficiency reasons, but
5141
+ * which do not support any syscache.
5075
5142
*/
5076
5143
bool
5077
5144
RelationIdIsInInitFile (Oid relationId )
5078
5145
{
5079
- if (relationId == TriggerRelidNameIndexId )
5146
+ if (relationId == TriggerRelidNameIndexId ||
5147
+ relationId == DatabaseNameIndexId )
5080
5148
{
5081
- /* If this Assert fails, we don't need this special case anymore. */
5149
+ /*
5150
+ * If this Assert fails, we don't need the applicable special case
5151
+ * anymore.
5152
+ */
5082
5153
Assert (!RelationSupportsSysCache (relationId ));
5083
5154
return true;
5084
5155
}
@@ -5106,38 +5177,30 @@ RelationIdIsInInitFile(Oid relationId)
5106
5177
* We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
5107
5178
* then release the lock in RelationCacheInitFilePostInvalidate. Caller must
5108
5179
* send any pending SI messages between those calls.
5109
- *
5110
- * Notice this deals only with the local init file, not the shared init file.
5111
- * The reason is that there can never be a "significant" change to the
5112
- * relcache entry of a shared relation; the most that could happen is
5113
- * updates of noncritical fields such as relpages/reltuples. So, while
5114
- * it's worth updating the shared init file from time to time, it can never
5115
- * be invalid enough to make it necessary to remove it.
5116
5180
*/
5117
5181
void
5118
5182
RelationCacheInitFilePreInvalidate (void )
5119
5183
{
5120
- char initfilename [MAXPGPATH ];
5184
+ char localinitfname [MAXPGPATH ];
5185
+ char sharedinitfname [MAXPGPATH ];
5121
5186
5122
- snprintf (initfilename , sizeof (initfilename ), "%s/%s" ,
5123
- DatabasePath , RELCACHE_INIT_FILENAME );
5187
+ if (DatabasePath )
5188
+ snprintf (localinitfname , sizeof (localinitfname ), "%s/%s" ,
5189
+ DatabasePath , RELCACHE_INIT_FILENAME );
5190
+ snprintf (sharedinitfname , sizeof (sharedinitfname ), "global/%s" ,
5191
+ RELCACHE_INIT_FILENAME );
5124
5192
5125
5193
LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
5126
5194
5127
- if (unlink (initfilename ) < 0 )
5128
- {
5129
- /*
5130
- * The file might not be there if no backend has been started since
5131
- * the last removal. But complain about failures other than ENOENT.
5132
- * Fortunately, it's not too late to abort the transaction if we can't
5133
- * get rid of the would-be-obsolete init file.
5134
- */
5135
- if (errno != ENOENT )
5136
- ereport (ERROR ,
5137
- (errcode_for_file_access (),
5138
- errmsg ("could not remove cache file \"%s\": %m" ,
5139
- initfilename )));
5140
- }
5195
+ /*
5196
+ * The files might not be there if no backend has been started since the
5197
+ * last removal. But complain about failures other than ENOENT with
5198
+ * ERROR. Fortunately, it's not too late to abort the transaction if we
5199
+ * can't get rid of the would-be-obsolete init file.
5200
+ */
5201
+ if (DatabasePath )
5202
+ unlink_initfile (localinitfname , ERROR );
5203
+ unlink_initfile (sharedinitfname , ERROR );
5141
5204
}
5142
5205
5143
5206
void
@@ -5163,13 +5226,9 @@ RelationCacheInitFileRemove(void)
5163
5226
struct dirent * de ;
5164
5227
char path [MAXPGPATH + 10 + sizeof (TABLESPACE_VERSION_DIRECTORY )];
5165
5228
5166
- /*
5167
- * We zap the shared cache file too. In theory it can't get out of sync
5168
- * enough to be a problem, but in data-corruption cases, who knows ...
5169
- */
5170
5229
snprintf (path , sizeof (path ), "global/%s" ,
5171
5230
RELCACHE_INIT_FILENAME );
5172
- unlink_initfile (path );
5231
+ unlink_initfile (path , LOG );
5173
5232
5174
5233
/* Scan everything in the default tablespace */
5175
5234
RelationCacheInitFileRemoveInDir ("base" );
@@ -5221,20 +5280,23 @@ RelationCacheInitFileRemoveInDir(const char *tblspcpath)
5221
5280
/* Try to remove the init file in each database */
5222
5281
snprintf (initfilename , sizeof (initfilename ), "%s/%s/%s" ,
5223
5282
tblspcpath , de -> d_name , RELCACHE_INIT_FILENAME );
5224
- unlink_initfile (initfilename );
5283
+ unlink_initfile (initfilename , LOG );
5225
5284
}
5226
5285
}
5227
5286
5228
5287
FreeDir (dir );
5229
5288
}
5230
5289
5231
5290
static void
5232
- unlink_initfile (const char * initfilename )
5291
+ unlink_initfile (const char * initfilename , int elevel )
5233
5292
{
5234
5293
if (unlink (initfilename ) < 0 )
5235
5294
{
5236
5295
/* It might not be there, but log any error other than ENOENT */
5237
5296
if (errno != ENOENT )
5238
- elog (LOG , "could not remove cache file \"%s\": %m" , initfilename );
5297
+ ereport (elevel ,
5298
+ (errcode_for_file_access (),
5299
+ errmsg ("could not remove cache file \"%s\": %m" ,
5300
+ initfilename )));
5239
5301
}
5240
5302
}
0 commit comments