@@ -248,6 +248,7 @@ static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
248
248
static void RelationClearRelation (Relation relation , bool rebuild );
249
249
250
250
static void RelationReloadIndexInfo (Relation relation );
251
+ static void RelationReloadNailed (Relation relation );
251
252
static void RelationFlushRelation (Relation relation );
252
253
static void RememberToFreeTupleDescAtEOX (TupleDesc td );
253
254
static void AtEOXact_cleanup (Relation relation , bool isCommit );
@@ -285,7 +286,7 @@ static void IndexSupportInitialize(oidvector *indclass,
285
286
static OpClassCacheEnt * LookupOpclassInfo (Oid operatorClassOid ,
286
287
StrategyNumber numSupport );
287
288
static void RelationCacheInitFileRemoveInDir (const char * tblspcpath );
288
- static void unlink_initfile (const char * initfilename );
289
+ static void unlink_initfile (const char * initfilename , int elevel );
289
290
static bool equalPartitionDescs (PartitionKey key , PartitionDesc partdesc1 ,
290
291
PartitionDesc partdesc2 );
291
292
@@ -2052,7 +2053,16 @@ RelationIdGetRelation(Oid relationId)
2052
2053
RelationReloadIndexInfo (rd );
2053
2054
else
2054
2055
RelationClearRelation (rd , true);
2055
- Assert (rd -> rd_isvalid );
2056
+
2057
+ /*
2058
+ * Normally entries need to be valid here, but before the relcache
2059
+ * has been initialized, not enough infrastructure exists to
2060
+ * perform pg_class lookups. The structure of such entries doesn't
2061
+ * change, but we still want to update the rd_rel entry. So
2062
+ * rd_isvalid = false is left in place for a later lookup.
2063
+ */
2064
+ Assert (rd -> rd_isvalid ||
2065
+ (rd -> rd_isnailed && !criticalRelcachesBuilt ));
2056
2066
}
2057
2067
return rd ;
2058
2068
}
@@ -2255,6 +2265,81 @@ RelationReloadIndexInfo(Relation relation)
2255
2265
relation -> rd_isvalid = true;
2256
2266
}
2257
2267
2268
+ /*
2269
+ * RelationReloadNailed - reload minimal information for nailed relations.
2270
+ *
2271
+ * The structure of a nailed relation can never change (which is good, because
2272
+ * we rely on knowing their structure to be able to read catalog content). But
2273
+ * some parts, e.g. pg_class.relfrozenxid, are still important to have
2274
+ * accurate content for. Therefore those need to be reloaded after the arrival
2275
+ * of invalidations.
2276
+ */
2277
+ static void
2278
+ RelationReloadNailed (Relation relation )
2279
+ {
2280
+ Assert (relation -> rd_isnailed );
2281
+
2282
+ /*
2283
+ * Redo RelationInitPhysicalAddr in case it is a mapped relation whose
2284
+ * mapping changed.
2285
+ */
2286
+ RelationInitPhysicalAddr (relation );
2287
+
2288
+ /* flag as needing to be revalidated */
2289
+ relation -> rd_isvalid = false;
2290
+
2291
+ /*
2292
+ * Can only reread catalog contents if in a transaction. If the relation
2293
+ * is currently open (not counting the nailed refcount), do so
2294
+ * immediately. Otherwise we've already marked the entry as possibly
2295
+ * invalid, and it'll be fixed when next opened.
2296
+ */
2297
+ if (!IsTransactionState () || relation -> rd_refcnt <= 1 )
2298
+ return ;
2299
+
2300
+ if (relation -> rd_rel -> relkind == RELKIND_INDEX )
2301
+ {
2302
+ /*
2303
+ * If it's a nailed-but-not-mapped index, then we need to re-read the
2304
+ * pg_class row to see if its relfilenode changed.
2305
+ */
2306
+ RelationReloadIndexInfo (relation );
2307
+ }
2308
+ else
2309
+ {
2310
+ /*
2311
+ * Reload a non-index entry. We can't easily do so if relcaches
2312
+ * aren't yet built, but that's fine because at that stage the
2313
+ * attributes that need to be current (like relfrozenxid) aren't yet
2314
+ * accessed. To ensure the entry will later be revalidated, we leave
2315
+ * it in invalid state, but allow use (cf. RelationIdGetRelation()).
2316
+ */
2317
+ if (criticalRelcachesBuilt )
2318
+ {
2319
+ HeapTuple pg_class_tuple ;
2320
+ Form_pg_class relp ;
2321
+
2322
+ /*
2323
+ * NB: Mark the entry as valid before starting to scan, to avoid
2324
+ * self-recursion when re-building pg_class.
2325
+ */
2326
+ relation -> rd_isvalid = true;
2327
+
2328
+ pg_class_tuple = ScanPgRelation (RelationGetRelid (relation ),
2329
+ true, false);
2330
+ relp = (Form_pg_class ) GETSTRUCT (pg_class_tuple );
2331
+ memcpy (relation -> rd_rel , relp , CLASS_TUPLE_SIZE );
2332
+ heap_freetuple (pg_class_tuple );
2333
+
2334
+ /*
2335
+ * Again mark as valid, to protect against concurrently arriving
2336
+ * invalidations.
2337
+ */
2338
+ relation -> rd_isvalid = true;
2339
+ }
2340
+ }
2341
+ }
2342
+
2258
2343
/*
2259
2344
* RelationDestroyRelation
2260
2345
*
@@ -2368,26 +2453,12 @@ RelationClearRelation(Relation relation, bool rebuild)
2368
2453
RelationCloseSmgr (relation );
2369
2454
2370
2455
/*
2371
- * Never, never ever blow away a nailed-in system relation, because we'd
2372
- * be unable to recover. However, we must redo RelationInitPhysicalAddr
2373
- * in case it is a mapped relation whose mapping changed.
2374
- *
2375
- * If it's a nailed-but-not-mapped index, then we need to re-read the
2376
- * pg_class row to see if its relfilenode changed. We do that immediately
2377
- * if we're inside a valid transaction and the relation is open (not
2378
- * counting the nailed refcount). Otherwise just mark the entry as
2379
- * possibly invalid, and it'll be fixed when next opened.
2456
+ * Treat nailed-in system relations separately, they always need to be
2457
+ * accessible, so we can't blow them away.
2380
2458
*/
2381
2459
if (relation -> rd_isnailed )
2382
2460
{
2383
- RelationInitPhysicalAddr (relation );
2384
-
2385
- if (relation -> rd_rel -> relkind == RELKIND_INDEX )
2386
- {
2387
- relation -> rd_isvalid = false; /* needs to be revalidated */
2388
- if (relation -> rd_refcnt > 1 && IsTransactionState ())
2389
- RelationReloadIndexInfo (relation );
2390
- }
2461
+ RelationReloadNailed (relation );
2391
2462
return ;
2392
2463
}
2393
2464
@@ -5904,24 +5975,26 @@ write_item(const void *data, Size len, FILE *fp)
5904
5975
5905
5976
/*
5906
5977
* Determine whether a given relation (identified by OID) is one of the ones
5907
- * we should store in the local relcache init file.
5978
+ * we should store in a relcache init file.
5908
5979
*
5909
5980
* We must cache all nailed rels, and for efficiency we should cache every rel
5910
5981
* that supports a syscache. The former set is almost but not quite a subset
5911
- * of the latter. Currently, we must special-case TriggerRelidNameIndexId,
5912
- * which RelationCacheInitializePhase3 chooses to nail for efficiency reasons,
5913
- * but which does not support any syscache.
5914
- *
5915
- * Note: this function is currently never called for shared rels. If it were,
5916
- * we'd probably also need a special case for DatabaseNameIndexId, which is
5917
- * critical but does not support a syscache.
5982
+ * of the latter. The special cases are relations where
5983
+ * RelationCacheInitializePhase2/3 chooses to nail for efficiency reasons, but
5984
+ * which do not support any syscache.
5918
5985
*/
5919
5986
bool
5920
5987
RelationIdIsInInitFile (Oid relationId )
5921
5988
{
5922
- if (relationId == TriggerRelidNameIndexId )
5989
+ if (relationId == SharedSecLabelRelationId ||
5990
+ relationId == TriggerRelidNameIndexId ||
5991
+ relationId == DatabaseNameIndexId ||
5992
+ relationId == SharedSecLabelObjectIndexId )
5923
5993
{
5924
- /* If this Assert fails, we don't need this special case anymore. */
5994
+ /*
5995
+ * If this Assert fails, we don't need the applicable special case
5996
+ * anymore.
5997
+ */
5925
5998
Assert (!RelationSupportsSysCache (relationId ));
5926
5999
return true;
5927
6000
}
@@ -5991,38 +6064,30 @@ RelationHasUnloggedIndex(Relation rel)
5991
6064
* We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
5992
6065
* then release the lock in RelationCacheInitFilePostInvalidate. Caller must
5993
6066
* send any pending SI messages between those calls.
5994
- *
5995
- * Notice this deals only with the local init file, not the shared init file.
5996
- * The reason is that there can never be a "significant" change to the
5997
- * relcache entry of a shared relation; the most that could happen is
5998
- * updates of noncritical fields such as relpages/reltuples. So, while
5999
- * it's worth updating the shared init file from time to time, it can never
6000
- * be invalid enough to make it necessary to remove it.
6001
6067
*/
6002
6068
void
6003
6069
RelationCacheInitFilePreInvalidate (void )
6004
6070
{
6005
- char initfilename [MAXPGPATH ];
6071
+ char localinitfname [MAXPGPATH ];
6072
+ char sharedinitfname [MAXPGPATH ];
6006
6073
6007
- snprintf (initfilename , sizeof (initfilename ), "%s/%s" ,
6008
- DatabasePath , RELCACHE_INIT_FILENAME );
6074
+ if (DatabasePath )
6075
+ snprintf (localinitfname , sizeof (localinitfname ), "%s/%s" ,
6076
+ DatabasePath , RELCACHE_INIT_FILENAME );
6077
+ snprintf (sharedinitfname , sizeof (sharedinitfname ), "global/%s" ,
6078
+ RELCACHE_INIT_FILENAME );
6009
6079
6010
6080
LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
6011
6081
6012
- if (unlink (initfilename ) < 0 )
6013
- {
6014
- /*
6015
- * The file might not be there if no backend has been started since
6016
- * the last removal. But complain about failures other than ENOENT.
6017
- * Fortunately, it's not too late to abort the transaction if we can't
6018
- * get rid of the would-be-obsolete init file.
6019
- */
6020
- if (errno != ENOENT )
6021
- ereport (ERROR ,
6022
- (errcode_for_file_access (),
6023
- errmsg ("could not remove cache file \"%s\": %m" ,
6024
- initfilename )));
6025
- }
6082
+ /*
6083
+ * The files might not be there if no backend has been started since the
6084
+ * last removal. But complain about failures other than ENOENT with
6085
+ * ERROR. Fortunately, it's not too late to abort the transaction if we
6086
+ * can't get rid of the would-be-obsolete init file.
6087
+ */
6088
+ if (DatabasePath )
6089
+ unlink_initfile (localinitfname , ERROR );
6090
+ unlink_initfile (sharedinitfname , ERROR );
6026
6091
}
6027
6092
6028
6093
void
@@ -6048,13 +6113,9 @@ RelationCacheInitFileRemove(void)
6048
6113
struct dirent * de ;
6049
6114
char path [MAXPGPATH + 10 + sizeof (TABLESPACE_VERSION_DIRECTORY )];
6050
6115
6051
- /*
6052
- * We zap the shared cache file too. In theory it can't get out of sync
6053
- * enough to be a problem, but in data-corruption cases, who knows ...
6054
- */
6055
6116
snprintf (path , sizeof (path ), "global/%s" ,
6056
6117
RELCACHE_INIT_FILENAME );
6057
- unlink_initfile (path );
6118
+ unlink_initfile (path , LOG );
6058
6119
6059
6120
/* Scan everything in the default tablespace */
6060
6121
RelationCacheInitFileRemoveInDir ("base" );
@@ -6106,20 +6167,23 @@ RelationCacheInitFileRemoveInDir(const char *tblspcpath)
6106
6167
/* Try to remove the init file in each database */
6107
6168
snprintf (initfilename , sizeof (initfilename ), "%s/%s/%s" ,
6108
6169
tblspcpath , de -> d_name , RELCACHE_INIT_FILENAME );
6109
- unlink_initfile (initfilename );
6170
+ unlink_initfile (initfilename , LOG );
6110
6171
}
6111
6172
}
6112
6173
6113
6174
FreeDir (dir );
6114
6175
}
6115
6176
6116
6177
static void
6117
- unlink_initfile (const char * initfilename )
6178
+ unlink_initfile (const char * initfilename , int elevel )
6118
6179
{
6119
6180
if (unlink (initfilename ) < 0 )
6120
6181
{
6121
6182
/* It might not be there, but log any error other than ENOENT */
6122
6183
if (errno != ENOENT )
6123
- elog (LOG , "could not remove cache file \"%s\": %m" , initfilename );
6184
+ ereport (elevel ,
6185
+ (errcode_for_file_access (),
6186
+ errmsg ("could not remove cache file \"%s\": %m" ,
6187
+ initfilename )));
6124
6188
}
6125
6189
}
0 commit comments