/* *in_progress_listisastackofongoingRelationBuildDesc()calls.CREATE *INDEXCONCURRENTLYmakescatalogchangesunderShareUpdateExclusiveLock. *Itcriticallyreliesoneachbackendabsorbingthosechangesnolaterthan *nexttransactionstart.Hence,RelationBuildDesc()loopsuntilitfinishes *withoutacceptingarelevantinvalidation.(Mostinvalidationconsumers *don'tdothis.)
*/ typedefstruct inprogressent
{
Oid reloid; /* OID of relation being built */ bool invalidated; /* whether an invalidation arrived for it */
} InProgressEnt;
#define RelationCacheDelete(RELATION) \ do { \
RelIdCacheEnt *hentry; \
hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
&((RELATION)->rd_id), \
HASH_REMOVE, NULL); \ if (hentry == NULL) \
elog(WARNING, "failed to delete relcache entry for OID %u", \
(RELATION)->rd_id); \
} while(0)
/* *Specialcacheforopclass-relatedinformation * *Note:onlydefaultsupportprocsgetcached,ie,thosewith *lefttype=righttype=opcintype.
*/ typedefstruct opclasscacheent
{
Oid opclassoid; /* lookup key: OID of opclass */ bool valid; /* set true after successful fill-in */
StrategyNumber numSupport; /* max # of support procs (from pg_am) */
Oid opcfamily; /* OID of opclass's family */
Oid opcintype; /* OID of opclass's declared input type */
RegProcedure *supportProcs; /* OIDs of support procedures */
} OpClassCacheEnt;
/* *Ifsomethinggoeswrongduringbackendstartup,wemightfindourselves *tryingtoreadpg_classbeforewe'veselectedadatabase.Thatain't *gonnawork,sobailoutwithausefulerrormessage.Ifthishappens, *itprobablymeansarelcacheentrythatneedstobenailedisn't.
*/ if (!OidIsValid(MyDatabaseId))
elog(FATAL, "cannot read pg_class without having selected a database");
/* initialize relation tuple form */
relation->rd_rel = relationForm;
/* and allocate attribute tuple form storage */
relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts); /* which we mark as a reference-counted tupdesc */
relation->rd_att->tdrefcount = 1;
/* *LookupanyAM-specificparsefunction;falloutifrelkindshouldnot *haveoptions.
*/ switch (relation->rd_rel->relkind)
{ case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_VIEW: case RELKIND_MATVIEW: case RELKIND_PARTITIONED_TABLE:
amoptsfn = NULL; break; case RELKIND_INDEX: case RELKIND_PARTITIONED_INDEX:
amoptsfn = relation->rd_indam->amoptions; break; default: return;
}
/* fill rd_att's type ID fields (compare heap.c's AddNewRelationTuple) */
relation->rd_att->tdtypeid =
relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;
relation->rd_att->tdtypmod = -1; /* just to be sure */
/* Update constraint/default info */ if (attp->attnotnull)
constr->has_not_null = true; if (attp->attgenerated == ATTRIBUTE_GENERATED_STORED)
constr->has_generated_stored = true; if (attp->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
constr->has_generated_virtual = true; if (attp->atthasdef)
ndef++;
/* If the column has a "missing" value, put it in the attrmiss array */ if (attp->atthasmissing)
{
Datum missingval; bool missingNull;
/* Do we have a missing value? */
missingval = heap_getattr(pg_attribute_tuple,
Anum_pg_attribute_attmissingval,
pg_attribute_desc->rd_att,
&missingNull); if (!missingNull)
{ /* Yes, fetch from the array */
MemoryContext oldcxt; bool is_null; int one = 1;
Datum missval;
/* CHECK and NOT NULLs */ if (relation->rd_rel->relchecks > 0 ||
(!is_catalog && constr->has_not_null))
CheckNNConstraintFetch(relation);
/* *Anynot-nullconstraintthatwasn'tmarkedinvalidby *CheckNNConstraintFetchmustnecessarilybevalid;makeitsointhe *CompactAttributearray.
*/ if (!is_catalog)
{ for (int i = 0; i < relation->rd_rel->relnatts; i++)
{
CompactAttribute *attr;
/* OK, now transfer the data into relation's rd_indexcxt. */
cached = (IndexAmRoutine *) MemoryContextAlloc(relation->rd_indexcxt, sizeof(IndexAmRoutine));
memcpy(cached, tmp, sizeof(IndexAmRoutine));
relation->rd_indam = cached;
pfree(tmp);
}
/* *Initializeindex-access-methodsupportdataforanindexrelation
*/ void
RelationInitIndexAccessInfo(Relation relation)
{
HeapTuple tuple;
Form_pg_am aform;
Datum indcollDatum;
Datum indclassDatum;
Datum indoptionDatum; bool isnull;
oidvector *indcoll;
oidvector *indclass;
int2vector *indoption;
MemoryContext indexcxt;
MemoryContext oldcontext; int indnatts; int indnkeyatts;
uint16 amsupport;
indnatts = RelationGetNumberOfAttributes(relation); if (indnatts != IndexRelationGetNumberOfAttributes(relation))
elog(ERROR, "relnatts disagrees with indnatts for index %u",
RelationGetRelid(relation));
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(relation);
/* *initializetherel-has-indexflag,usinghardwiredknowledge
*/ if (IsBootstrapProcessingMode())
{ /* In bootstrap mode, we have no indexes */
relation->rd_rel->relhasindex = false;
} else
{ /* Otherwise, all the rels formrdesc is used for have indexes */
relation->rd_rel->relhasindex = true;
}
/* Should be called only for invalidated, live indexes */
Assert((relation->rd_rel->relkind == RELKIND_INDEX ||
relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) &&
!relation->rd_isvalid &&
relation->rd_droppedSubid == InvalidSubTransactionId);
/* *Readthepg_classrow * *Don'ttrytouseanindexscanofpg_class_oid_indextoreloadtheinfo *forpg_class_oid_index... */ indexOK=(RelationGetRelid(relation)!=ClassOidIndexId); pg_class_tuple=ScanPgRelation(RelationGetRelid(relation),indexOK,false); if(!HeapTupleIsValid(pg_class_tuple)) elog(ERROR,"couldnotfindpg_classtupleforindex%u", RelationGetRelid(relation)); relp=(Form_pg_class)GETSTRUCT(pg_class_tuple); memcpy(relation->rd_rel,relp,CLASS_TUPLE_SIZE); /* Reload reloptions in case they changed */ if (relation->rd_options)
pfree(relation->rd_options);
RelationParseRelOptions(relation, pg_class_tuple); /* done with pg_class tuple */
heap_freetuple(pg_class_tuple); /* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation);
tuple = SearchSysCache1(INDEXRELID,
ObjectIdGetDatum(RelationGetRelid(relation))); if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u",
RelationGetRelid(relation));
index = (Form_pg_index) GETSTRUCT(tuple);
/* Copy xmin too, as that is needed to make sense of indcheckxmin */
HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
HeapTupleHeaderGetXmin(tuple->t_data));
ReleaseSysCache(tuple);
}
/* Okay, now it's valid again */
relation->rd_isvalid = true;
}
/* *RelationReloadNailed-reloadminimalinformationfornailedrelations. * *Thestructureofanailedrelationcanneverchange(whichisgood,because *werelyonknowingtheirstructuretobeabletoreadcatalogcontent).But *someparts,e.g.pg_class.relfrozenxid,arestillimportanttohave *accuratecontentfor.Thereforethoseneedtobereloadedafterthearrival *ofinvalidations.
*/ staticvoid
RelationReloadNailed(Relation relation)
{ /* Should be called only for invalidated, nailed relations */
Assert(!relation->rd_isvalid);
Assert(relation->rd_isnailed); /* nailed indexes are handled by RelationReloadIndexInfo() */
Assert(relation->rd_rel->relkind == RELKIND_RELATION);
AssertCouldGetRelation();
/* break mutual link with stats entry */
pgstat_unlink_relation(relation);
/* *Freeallthesubsidiarydatastructuresoftherelcacheentry,thenthe *entryitself.
*/ if (relation->rd_rel)
pfree(relation->rd_rel); /* can't use DecrTupleDescRefCount here */
Assert(relation->rd_att->tdrefcount > 0); if (--relation->rd_att->tdrefcount == 0)
{ /* *IfweRebuiltarelcacheentryduringatransactionthenits *possiblewedidthatbecausetheTupDescchangedastheresultof *anALTERTABLEthatranatlessthanAccessExclusiveLock.It's *possiblesomeonecopiedthatTupDesc,inwhichcasethecopywould *pointtofree'dmemory.Soifwerebuildanentrywekeepthe *TupDescarounduntilendoftransaction,tobesafe.
*/ if (remember_tupdesc)
RememberToFreeTupleDescAtEOX(relation->rd_att); else
FreeTupleDesc(relation->rd_att);
}
FreeTriggerDesc(relation->trigdesc);
list_free_deep(relation->rd_fkeylist);
list_free(relation->rd_indexlist);
list_free(relation->rd_statlist);
bms_free(relation->rd_keyattr);
bms_free(relation->rd_pkattr);
bms_free(relation->rd_idattr);
bms_free(relation->rd_hotblockingattr);
bms_free(relation->rd_summarizedattr); if (relation->rd_pubdesc)
pfree(relation->rd_pubdesc); if (relation->rd_options)
pfree(relation->rd_options); if (relation->rd_indextuple)
pfree(relation->rd_indextuple); if (relation->rd_amcache)
pfree(relation->rd_amcache); if (relation->rd_fdwroutine)
pfree(relation->rd_fdwroutine); if (relation->rd_indexcxt)
MemoryContextDelete(relation->rd_indexcxt); if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt); if (relation->rd_rsdesc)
MemoryContextDelete(relation->rd_rsdesc->rscxt); if (relation->rd_partkeycxt)
MemoryContextDelete(relation->rd_partkeycxt); if (relation->rd_pdcxt)
MemoryContextDelete(relation->rd_pdcxt); if (relation->rd_pddcxt)
MemoryContextDelete(relation->rd_pddcxt); if (relation->rd_partcheckcxt)
MemoryContextDelete(relation->rd_partcheckcxt);
pfree(relation);
}
keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
keep_policies = equalRSDesc(relation->rd_rsdesc, newrel->rd_rsdesc); /* partkey is immutable once set up, so we can always keep it */
keep_partkey = (relation->rd_partkey != NULL);
if (!debug_discard) /* Any RelationBuildDesc() on the stack must start over. */ for (i = 0; i < in_progress_list_len; i++)
in_progress_list[i].invalidated = true;
}
/* *Unlesstheeoxact_list[]overflowed,weonlyneedtoexaminetherels *listedinit.Otherwisefallbackonahash_seq_searchscan. * *Forsimplicity,eoxact_list[]entriesarenotdeletedtillendof *top-leveltransaction,eventhoughwecouldremovethemat *subtransactionendinsomecases,orremoverelationsfromthelistif *theyareclearedforotherreasons.Thereforeweshouldexpectthe *casethatlistentriesarenotfoundinthehashtable;ifnot,there's *nothingtodoforthem.
*/ if (eoxact_list_overflowed)
{
hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
{
AtEOXact_cleanup(idhentry->reldesc, isCommit);
}
} else
{ for (i = 0; i < eoxact_list_len; i++)
{
idhentry = (RelIdCacheEnt *) hash_search(RelationIdCache,
&eoxact_list[i],
HASH_FIND,
NULL); if (idhentry != NULL)
AtEOXact_cleanup(idhentry->reldesc, isCommit);
}
}
if (EOXactTupleDescArrayLen > 0)
{
Assert(EOXactTupleDescArray != NULL); for (i = 0; i < NextEOXactTupleDescNum; i++)
FreeTupleDesc(EOXactTupleDescArray[i]);
pfree(EOXactTupleDescArray);
EOXactTupleDescArray = NULL;
}
/* Now we're out of the transaction and can clear the lists */
eoxact_list_len = 0;
eoxact_list_overflowed = false;
NextEOXactTupleDescNum = 0;
EOXactTupleDescArrayLen = 0;
}
/* *Likewise,updateordropanynew-relfilenumber-in-subtransactionrecord *ordroprecord.
*/ if (relation->rd_newRelfilelocatorSubid == mySubid)
{ if (isCommit)
relation->rd_newRelfilelocatorSubid = parentSubid; else
relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
}
if (relation->rd_firstRelfilelocatorSubid == mySubid)
{ if (isCommit)
relation->rd_firstRelfilelocatorSubid = parentSubid; else
relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
}
if (relation->rd_droppedSubid == mySubid)
{ if (isCommit)
relation->rd_droppedSubid = parentSubid; else
relation->rd_droppedSubid = InvalidSubTransactionId;
}
}
/* *RelationBuildLocalRelation *Buildarelcacheentryforanabout-to-be-createdrelation, *andenteritintotherelcache.
*/
Relation
RelationBuildLocalRelation(constchar *relname,
Oid relnamespace,
TupleDesc tupDesc,
Oid relid,
Oid accessmtd,
RelFileNumber relfilenumber,
Oid reltablespace, bool shared_relation, bool mapped_relation, char relpersistence, char relkind)
{
Relation rel;
MemoryContext oldcxt; int natts = tupDesc->natts; int i; bool has_not_null; bool nailit;
Assert(natts >= 0);
/* *checkforcreationofarelthatmustbenailedincache. * *XXXthislisthadbettermatchtherelationsspeciallyhandledin *RelationCacheInitializePhase2/3.
*/ switch (relid)
{ case DatabaseRelationId: case AuthIdRelationId: case AuthMemRelationId: case RelationRelationId: case AttributeRelationId: case ProcedureRelationId: case TypeRelationId:
nailit = true; break; default:
nailit = false; break;
}
/* *checkthathardwiredlistofsharedrelsmatcheswhat'sinthe *bootstrap.bkifile.Ifyougetafailurehereduringinitdb,you *probablyneedtofixIsSharedRelation()tomatchwhateveryou'vedone *tothesetofsharedrelations.
*/ if (shared_relation != IsSharedRelation(relid))
elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
relname, relid);
/* Shared relations had better be mapped, too */
Assert(mapped_relation || !shared_relation);
/* *switchtothecachecontexttocreatetherelcacheentry.
*/ if (!CacheMemoryContext)
CreateCacheMemoryContext();
/* make sure relation is marked as having no open file yet */
rel->rd_smgr = NULL;
/* mark it nailed if appropriate */
rel->rd_isnailed = nailit;
rel->rd_refcnt = nailit ? 1 : 0;
/* it's being created in this transaction */
rel->rd_createSubid = GetCurrentSubTransactionId();
rel->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
rel->rd_droppedSubid = InvalidSubTransactionId;
/* *createanewtupledescriptorfromtheonepassedin.Wedothis *partlytocopyitintothecachecontext,andpartlybecausethenew *relationcan'thaveanydefaultsorconstraintsyet;theyhavetobe *addedinlatersteps,becausetheyrequireadditionstomultiplesystem *catalogs.Wecancopyattnotnullconstraintshere,however.
*/
rel->rd_att = CreateTupleDescCopy(tupDesc);
rel->rd_att->tdrefcount = 1; /* mark as refcounted */
has_not_null = false; for (i = 0; i < natts; i++)
{
Form_pg_attribute satt = TupleDescAttr(tupDesc, i);
Form_pg_attribute datt = TupleDescAttr(rel->rd_att, i);
for (i = 0; i < natts; i++)
TupleDescAttr(rel->rd_att, i)->attrelid = relid;
rel->rd_rel->reltablespace = reltablespace;
if (mapped_relation)
{
rel->rd_rel->relfilenode = InvalidRelFileNumber; /* Add it to the active mapping information */
RelationMapUpdateMap(relid, relfilenumber, shared_relation, true);
} else
rel->rd_rel->relfilenode = relfilenumber;
if (!IsBinaryUpgrade)
{ /* Allocate a new relfilenumber */
newrelfilenumber = GetNewRelFileNumber(relation->rd_rel->reltablespace,
NULL, persistence);
} elseif (relation->rd_rel->relkind == RELKIND_INDEX)
{ if (!OidIsValid(binary_upgrade_next_index_pg_class_relfilenumber))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("index relfilenumber value not set when in binary upgrade mode")));
newrelfilenumber = binary_upgrade_next_index_pg_class_relfilenumber;
binary_upgrade_next_index_pg_class_relfilenumber = InvalidOid;
} elseif (relation->rd_rel->relkind == RELKIND_RELATION)
{ if (!OidIsValid(binary_upgrade_next_heap_pg_class_relfilenumber))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("heap relfilenumber value not set when in binary upgrade mode")));
newrelfilenumber = binary_upgrade_next_heap_pg_class_relfilenumber;
binary_upgrade_next_heap_pg_class_relfilenumber = InvalidOid;
} else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unexpected request for new relfilenumber in binary upgrade mode")));
if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
{
table_relation_set_new_filelocator(relation, &newrlocator,
persistence,
&freezeXid, &minmulti);
} elseif (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
{ /* handle these directly, at least for now */
SMgrRelation srel;
srel = RelationCreateStorage(newrlocator, persistence, true);
smgrclose(srel);
} else
{ /* we shouldn't be called for anything else */
elog(ERROR, "relation \"%s\" does not have storage",
RelationGetRelationName(relation));
}
/* *Ifwe'redealingwithamappedindex,pg_class.relfilenodedoesn't *change;insteadwehavetosendtheupdatetotherelationmapper. * *Formappedindexes,wedon'tactuallychangethepg_classentryatall; *thisisessentialwhenreindexingpg_classitself.Thatleavesuswith *possibly-inaccuratevaluesofrelpagesetc,butthosewillbefixedup *later.
*/ if (RelationIsMapped(relation))
{ /* This case is only supported for indexes */
Assert(relation->rd_rel->relkind == RELKIND_INDEX);
/* Since we're not updating pg_class, these had better not change */
Assert(classform->relfrozenxid == freezeXid);
Assert(classform->relminmxid == minmulti);
Assert(classform->relpersistence == persistence);
/* relowner had better be OK now, else we'll loop forever */ if (relation->rd_rel->relowner == InvalidOid)
elog(ERROR, "invalid relowner in pg_class entry for \"%s\"",
RelationGetRelationName(relation));
/* *Loadanydefaultattributevaluedefinitionsfortherelation. * *ndefisthenumberofattributesthatweremarkedatthasdef. * *Note:wedon'tmakeitaharderrortobemissingsomepg_attrdefrecords. *Wecanlimpalongaslongasnothingneedstousethedefaultvalue.Code *thatfailstofindanexpectedAttrDefaultrecordshouldthrowanerror.
*/ staticvoid
AttrDefaultFetch(Relation relation, int ndef)
{
AttrDefault *attrdef;
Relation adrel;
SysScanDesc adscan;
ScanKeyData skey;
HeapTuple htup; int found = 0;
/* Allocate array with room for as many entries as expected */
attrdef = (AttrDefault *)
MemoryContextAllocZero(CacheMemoryContext,
ndef * sizeof(AttrDefault));
while (HeapTupleIsValid(htup = systable_getnext(adscan)))
{
Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup);
Datum val;
bool isnull;
/* protect limited size of array */ if (found >= ndef)
{
elog(WARNING, "unexpected pg_attrdef record found for attribute %d of relation \"%s\"",
adform->adnum, RelationGetRelationName(relation)); break;
}
val = fastgetattr(htup,
Anum_pg_attrdef_adbin,
adrel->rd_att, &isnull); if (isnull)
elog(WARNING, "null adbin for attribute %d of relation \"%s\"",
adform->adnum, RelationGetRelationName(relation)); else
{ /* detoast and convert to cstring in caller's context */ char *s = TextDatumGetCString(val);
/* *Loadanycheckconstraintsfortherelation,andupdatenot-nullvalidity *ofinvalidconstraints. * *Aswithdefaults,ifwedon'tfindtheexpectednumberofthem,justwarn *here.TheexecutorshouldthrowanerrorifanINSERT/UPDATEisattempted.
*/ staticvoid
CheckNNConstraintFetch(Relation relation)
{
ConstrCheck *check; int ncheck = relation->rd_rel->relchecks;
Relation conrel;
SysScanDesc conscan;
ScanKeyData skey[1];
HeapTuple htup; int found = 0;
/* Allocate array with room for as many entries as expected, if needed */ if (ncheck > 0)
check = (ConstrCheck *)
MemoryContextAllocZero(CacheMemoryContext,
ncheck * sizeof(ConstrCheck)); else
check = NULL;
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
Datum val;
bool isnull;
/* *Ifthisisanot-nullconstraint,thenonlylookatitifit's *invalid,andifso,marktheTupleDescentryasknowninvalid. *Otherwisemoveon.We'llmarkanyremainingcolumnsthatarestill *inUNKNOWNstateasknownvalidlater.Thisallowsusnottohave *toextracttheattnumfromthisconstrainttupleinthevast *majorityofcases.
*/ if (conform->contype == CONSTRAINT_NOTNULL)
{ if (!conform->convalidated)
{
AttrNumber attnum;
/* For what follows, consider check constraints only */ if (conform->contype != CONSTRAINT_CHECK) continue;
/* protect limited size of array */ if (found >= ncheck)
{
elog(WARNING, "unexpected pg_constraint record found for relation \"%s\"",
RelationGetRelationName(relation)); break;
}
/* Grab and test conbin is actually set */
val = fastgetattr(htup,
Anum_pg_constraint_conbin,
conrel->rd_att, &isnull); if (isnull)
elog(WARNING, "null conbin for relation \"%s\"",
RelationGetRelationName(relation)); else
{ /* detoast and convert to cstring in caller's context */ char *s = TextDatumGetCString(val);
/* Quick exit if we already computed the list. */ if (relation->rd_fkeyvalid) return relation->rd_fkeylist;
/* *Webuildthelistweintendtoreturn(inthecaller'scontext)while *doingthescan.Aftersuccessfullycompletingthescan,wecopythat *listintotherelcacheentry.Thisavoidscache-contextmemoryleakage *ifwegetsomesortoferrorpartwaythrough.
*/
result = NIL;
/* Prepare to scan pg_constraint for entries having conrelid = this rel. */
ScanKeyInit(&skey,
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
/* Now save a copy of the completed list in the relcache entry. */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
oldlist = relation->rd_fkeylist;
relation->rd_fkeylist = copyObject(result);
relation->rd_fkeyvalid = true;
MemoryContextSwitchTo(oldcxt);
/* Don't leak the old list, if there is one */
list_free_deep(oldlist);
/* Quick exit if we already computed the list. */ if (relation->rd_indexvalid) return list_copy(relation->rd_indexlist);
/* *Webuildthelistweintendtoreturn(inthecaller'scontext)while *doingthescan.Aftersuccessfullycompletingthescan,wecopythat *listintotherelcacheentry.Thisavoidscache-contextmemoryleakage *ifwegetsomesortoferrorpartwaythrough.
*/
result = NIL;
/* Prepare to scan pg_index for entries having indrelid = this rel. */
ScanKeyInit(&skey,
Anum_pg_index_indrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
while (HeapTupleIsValid(htup = systable_getnext(indscan)))
{
Oid oid = ((Form_pg_statistic_ext) GETSTRUCT(htup))->oid;
result = lappend_oid(result, oid);
}
systable_endscan(indscan);
table_close(indrel, AccessShareLock);
/* Sort the result list into OID order, per API spec. */
list_sort(result, list_oid_cmp);
/* Now save a copy of the completed list in the relcache entry. */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
oldlist = relation->rd_statlist;
relation->rd_statlist = list_copy(result);
/* Don't leak the old list, if there is one */
list_free(oldlist);
return result;
}
/* *RelationGetPrimaryKeyIndex--getOIDoftherelation'sprimarykeyindex * *ReturnsInvalidOidifthereisnosuchindex,oriftheprimarykeyis *DEFERRABLEandthecallerisn'tOKwiththat.
*/
Oid
RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
{
List *ilist;
if (!relation->rd_indexvalid)
{ /* RelationGetIndexList does the heavy lifting. */
ilist = RelationGetIndexList(relation);
list_free(ilist); Assert(relation->rd_indexvalid);
}
/* *RelationGetReplicaIndex--getOIDoftherelation'sreplicaidentityindex * *ReturnsInvalidOidifthereisnosuchindex.
*/
Oid
RelationGetReplicaIndex(Relation relation)
{
List *ilist;
if (!relation->rd_indexvalid)
{ /* RelationGetIndexList does the heavy lifting. */
ilist = RelationGetIndexList(relation);
list_free(ilist); Assert(relation->rd_indexvalid);
}
return relation->rd_replidindex;
}
/* *RelationGetIndexExpressions--gettheindexexpressionsforanindex * *Wecachetheresultoftransformingpg_index.indexprsintoanodetree. *Iftherelisnotanindexorhasnoexpressionalcolumns,wereturnNIL. *Otherwise,thereturnedtreeiscopiedintothecaller'smemorycontext. *(Wedon'twanttoreturnapointertotherelcachecopy,sinceitcould *disappearduetorelcacheinvalidation.)
*/
List *
RelationGetIndexExpressions(Relation relation)
{
List *result;
Datum exprsDatum;
bool isnull; char *exprsString;
MemoryContext oldcxt;
/* Quick exit if we already computed the result. */ if (relation->rd_indexprs) return copyObject(relation->rd_indexprs);
/* Quick exit if there is nothing to do. */ if (relation->rd_indextuple == NULL ||
heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL)) return NIL;
/* May as well fix opfuncids too */
fix_opfuncids((Node *) result);
/* Now save a copy of the completed tree in the relcache entry. */
oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
relation->rd_indexprs = copyObject(result);
MemoryContextSwitchTo(oldcxt);
return result;
}
/* *RelationGetDummyIndexExpressions--getdummyexpressionsforanindex * *Returnalistofdummyexpressions(justConstnodes)withthesame *types/typmods/collationsastheindex'srealexpressions.Thisis *usefulinsituationswherewedon'twanttorunanyuser-definedcode.
*/
List *
RelationGetDummyIndexExpressions(Relation relation)
{
List *result;
Datum exprsDatum;
bool isnull; char *exprsString;
List *rawExprs;
ListCell *lc;
/* Quick exit if there is nothing to do. */ if (relation->rd_indextuple == NULL ||
heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL)) return NIL;
/* Extract raw node tree(s) from index tuple. */
exprsDatum = heap_getattr(relation->rd_indextuple,
Anum_pg_index_indexprs,
GetPgIndexDescriptor(),
&isnull); Assert(!isnull);
exprsString = TextDatumGetCString(exprsDatum);
rawExprs = (List *) stringToNode(exprsString);
pfree(exprsString);
/* Construct null Consts; the typlen and typbyval are arbitrary. */
result = NIL;
foreach(lc, rawExprs)
{
Node *rawExpr = (Node *) lfirst(lc);
/* *RelationGetIndexPredicate--gettheindexpredicateforanindex * *Wecachetheresultoftransformingpg_index.indpredintoanimplicit-AND *nodetree(suitableforuseinplanning). *Iftherelisnotanindexorhasnopredicate,wereturnNIL. *Otherwise,thereturnedtreeiscopiedintothecaller'smemorycontext. *(Wedon'twanttoreturnapointertotherelcachecopy,sinceitcould *disappearduetorelcacheinvalidation.)
*/
List *
RelationGetIndexPredicate(Relation relation)
{
List *result;
Datum predDatum;
bool isnull; char *predString;
MemoryContext oldcxt;
/* Quick exit if we already computed the result. */ if (relation->rd_indpred) return copyObject(relation->rd_indpred);
/* Quick exit if there is nothing to do. */ if (relation->rd_indextuple == NULL ||
heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL)) return NIL;
result = (List *) canonicalize_qual((Expr *) result, false);
/* Also convert to implicit-AND format */
result = make_ands_implicit((Expr *) result);
/* May as well fix opfuncids too */
fix_opfuncids((Node *) result);
/* Now save a copy of the completed tree in the relcache entry. */
oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
relation->rd_indpred = copyObject(result);
MemoryContextSwitchTo(oldcxt);
return result;
}
/* *RelationGetIndexAttrBitmap--getabitmapofindexattributenumbers * *Theresulthasabitsetforeachattributeusedanywhereintheindex *definitionsofalltheindexesonthisrelation.(Thisincludesnotonly *simpleindexkeys,butattributesusedinexpressionsandpartial-index *predicates.) * *DependingonattrKind,abitmapcoveringattnumsforcertaincolumnsis *returned: *INDEX_ATTR_BITMAP_KEYColumnsinnon-partialuniqueindexesnot *inexpressions(i.e.,usableforFKs) *INDEX_ATTR_BITMAP_PRIMARY_KEYColumnsinthetable'sprimarykey *(beware:evenifPKisdeferrable!) *INDEX_ATTR_BITMAP_IDENTITY_KEYColumnsinthetable'sreplicaidentity *index(emptyifFULL) *INDEX_ATTR_BITMAP_HOT_BLOCKINGColumnsthatblockupdatesfrombeingHOT *INDEX_ATTR_BITMAP_SUMMARIZEDColumnsincludedinsummarizingindexes * *AttributenumbersareoffsetbyFirstLowInvalidHeapAttributeNumbersothat *wecanincludesystemattributes(e.g.,OID)inthebitmaprepresentation. * *Deferredindexesareconsideredfortheprimarykey,butnotforreplica *identity. * *CallerhadbetterholdatleastRowExclusiveLockonthetargetrelation *toensureitissafe(deadlock-free)forustotakelocksontherelation's *indexes.NotethatsincetheintroductionofCREATEINDEXCONCURRENTLY, *thatlockleveldoesn'tguaranteeastablesetofindexes,sowehaveto *bepreparedtoretryhereincaseofachangeinthesetofindexes. * *Thereturnedresultispalloc'dinthecaller'smemorycontextandshould *bebms_free'dwhennotneededanymore.
*/
Bitmapset *
RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
{
Bitmapset *uindexattrs; /* columns in unique indexes */
Bitmapset *pkindexattrs; /* columns in the primary index */
Bitmapset *idindexattrs; /* columns in the replica identity */
Bitmapset *hotblockingattrs; /* columns with HOT blocking indexes */
Bitmapset *summarizedattrs; /* columns with summarizing indexes */
List *indexoidlist;
List *newindexoidlist;
Oid relpkindex;
Oid relreplindex;
ListCell *l;
MemoryContext oldcxt;
/* Quick exit if we already computed the result. */ if (relation->rd_attrsvalid)
{ switch (attrKind)
{ case INDEX_ATTR_BITMAP_KEY: return bms_copy(relation->rd_keyattr); case INDEX_ATTR_BITMAP_PRIMARY_KEY: return bms_copy(relation->rd_pkattr); case INDEX_ATTR_BITMAP_IDENTITY_KEY: return bms_copy(relation->rd_idattr); case INDEX_ATTR_BITMAP_HOT_BLOCKING: return bms_copy(relation->rd_hotblockingattr); case INDEX_ATTR_BITMAP_SUMMARIZED: return bms_copy(relation->rd_summarizedattr); default:
elog(ERROR, "unknown attrKind %u", attrKind);
}
}
/* Fast path if definitely no indexes */ if (!RelationGetForm(relation)->relhasindex) returnNULL;
if (isKey && i < indexDesc->rd_index->indnkeyatts)
uindexattrs = bms_add_member(uindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
if (isPK && i < indexDesc->rd_index->indnkeyatts)
pkindexattrs = bms_add_member(pkindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
if (isIDKey && i < indexDesc->rd_index->indnkeyatts)
idindexattrs = bms_add_member(idindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
}
}
/* Collect all attributes used in expressions, too */
pull_varattnos(indexExpressions, 1, attrs);
/* Collect all attributes in the index predicate, too */
pull_varattnos(indexPredicate, 1, attrs);
index_close(indexDesc, AccessShareLock);
}
/* *Duringoneoftheindex_opensintheaboveloop,wemighthavereceived *arelcacheflusheventonthisrelcacheentry,whichmighthavebeen *signalingachangeintherel'sindexlist.Ifso,we'dbetterstart *overtoensurewedeliverup-to-dateattributebitmaps.
*/
newindexoidlist = RelationGetIndexList(relation); if (equal(indexoidlist, newindexoidlist) &&
relpkindex == relation->rd_pkindex &&
relreplindex == relation->rd_replidindex)
{ /* Still the same index set, so proceed */
list_free(newindexoidlist);
list_free(indexoidlist);
} else
{ /* Gotta do it over ... might as well not leak memory */
list_free(newindexoidlist);
list_free(indexoidlist);
bms_free(uindexattrs);
bms_free(pkindexattrs);
bms_free(idindexattrs);
bms_free(hotblockingattrs);
bms_free(summarizedattrs);
goto restart;
}
/* Don't leak the old values of these bitmaps, if any */
relation->rd_attrsvalid = false;
bms_free(relation->rd_keyattr);
relation->rd_keyattr = NULL;
bms_free(relation->rd_pkattr);
relation->rd_pkattr = NULL;
bms_free(relation->rd_idattr);
relation->rd_idattr = NULL;
bms_free(relation->rd_hotblockingattr);
relation->rd_hotblockingattr = NULL;
bms_free(relation->rd_summarizedattr);
relation->rd_summarizedattr = NULL;
/* We return our original working copy for caller to play with */ switch (attrKind)
{ case INDEX_ATTR_BITMAP_KEY: return uindexattrs; case INDEX_ATTR_BITMAP_PRIMARY_KEY: return pkindexattrs; case INDEX_ATTR_BITMAP_IDENTITY_KEY: return idindexattrs; case INDEX_ATTR_BITMAP_HOT_BLOCKING: return hotblockingattrs; case INDEX_ATTR_BITMAP_SUMMARIZED: return summarizedattrs; default:
elog(ERROR, "unknown attrKind %u", attrKind); return NULL;
}
}
/* *RelationGetIdentityKeyBitmap--getabitmapofreplicaidentityattribute *numbers * *Abitmapofindexattributenumbersfortheconfiguredreplicaidentity *indexisreturned. * *SeealsocommentsofRelationGetIndexAttrBitmap(). * *Thisisaspecialpurposefunctionusedduringlogicalreplication.Here, *unlikeRelationGetIndexAttrBitmap(),wedon'tacquirealockontherequired *indexaswebuildthecacheentryusingahistoricsnapshotandallthe *laterchangesareabsorbedwhiledecodingWAL.Duetothisreason,wedon't *needtoretryhereincaseofachangeinthesetofindexes.
*/
Bitmapset *
RelationGetIdentityKeyBitmap(Relation relation)
{
Bitmapset *idindexattrs = NULL; /* columns in the replica identity */
Relation indexDesc; int i;
Oid replidindex;
MemoryContext oldcxt;
/* Quick exit if we already computed the result */ if (relation->rd_idattr != NULL) return bms_copy(relation->rd_idattr);
/* Fast path if definitely no indexes */ if (!RelationGetForm(relation)->relhasindex) return NULL;
/* Historic snapshot must be set. */
Assert(HistoricSnapshotActive());
replidindex = RelationGetReplicaIndex(relation);
/* Fall out if there is no replica identity index */ if (!OidIsValid(replidindex)) return NULL;
/* Look up the description for the replica identity index */
indexDesc = RelationIdGetRelation(replidindex);
if (!RelationIsValid(indexDesc))
elog(ERROR, "could not open relation with OID %u",
relation->rd_replidindex);
/* Add referenced attributes to idindexattrs */ for (i = 0; i < indexDesc->rd_index->indnatts; i++)
{ int attrnum = indexDesc->rd_index->indkey.values[i];
/* *Wedon'tincludenon-keycolumnsintoidindexattrsbitmaps.See *RelationGetIndexAttrBitmap.
*/ if (attrnum != 0)
{ if (i < indexDesc->rd_index->indnkeyatts)
idindexattrs = bms_add_member(idindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
}
}
RelationClose(indexDesc);
/* Don't leak the old values of these bitmaps, if any */
bms_free(relation->rd_idattr);
relation->rd_idattr = NULL;
/* Now save copy of the bitmap in the relcache entry */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
relation->rd_idattr = bms_copy(idindexattrs);
MemoryContextSwitchTo(oldcxt);
/* We return our original working copy for caller to play with */ return idindexattrs;
}
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
Datum val; bool isnull;
ArrayType *arr; int nelem;
/* We want the exclusion constraint owning the index */ if ((conform->contype != CONSTRAINT_EXCLUSION &&
!(conform->conperiod && (conform->contype == CONSTRAINT_PRIMARY
|| conform->contype == CONSTRAINT_UNIQUE))) ||
conform->conindid != RelationGetRelid(indexRelation)) continue;
/* There should be only one */ if (found)
elog(ERROR, "unexpected exclusion constraint record found for rel %s",
RelationGetRelationName(indexRelation));
found = true;
/* Extract the operator OIDS from conexclop */
val = fastgetattr(htup,
Anum_pg_constraint_conexclop,
conrel->rd_att, &isnull); if (isnull)
elog(ERROR, "null conexclop for rel %s",
RelationGetRelationName(indexRelation));
arr = DatumGetArrayTypeP(val); /* ensure not toasted */
nelem = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 ||
nelem != indnkeyatts ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != OIDOID)
elog(ERROR, "conexclop is not a 1-D Oid array");
if (!found)
elog(ERROR, "exclusion constraint record missing for rel %s",
RelationGetRelationName(indexRelation));
/* We need the func OIDs and strategy numbers too */ for (i = 0; i < indnkeyatts; i++)
{
funcs[i] = get_opcode(ops[i]);
strats[i] = get_op_opfamily_strategy(ops[i],
indexRelation->rd_opfamily[i]); /* shouldn't fail, since it was checked at index creation */ if (strats[i] == InvalidStrategy)
elog(ERROR, "could not find strategy for operator %u in family %u",
ops[i], indexRelation->rd_opfamily[i]);
}
/* Save a copy of the results in the relcache entry. */
oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
indexRelation->rd_exclops = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
indexRelation->rd_exclprocs = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
indexRelation->rd_exclstrats = (uint16 *) palloc(sizeof(uint16) * indnkeyatts);
memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * indnkeyatts);
memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * indnkeyatts);
memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * indnkeyatts);
MemoryContextSwitchTo(oldcxt);
}
/* *Getthepublicationinformationforthegivenrelation. * *Traverseallthepublicationswhichtherelationisintogetthe *publicationactionsandvalidate: *1.Therowfilterexpressionsforsuchpublicationsifany.Weconsiderthe *rowfilterexpressionasinvalidifitreferencesanycolumnwhichisnot *partofREPLICAIDENTITY. *2.Thecolumnlistforsuchpublicationifany.Weconsiderthecolumnlist *invalidifREPLICAIDENTITYcontainsanycolumnthatisnotpartofit. *3.Thegeneratedcolumnsoftherelationforsuchpublications.Weconsider *anyreferenceofanunpublishedgeneratedcolumninREPLICAIDENTITYas *invalid. * *Toavoidfetchingthepublicationinformationrepeatedly,wecachethe *publicationactions,rowfiltervalidationinformation,columnlist *validationinformation,andgeneratedcolumnvalidationinformation.
*/ void
RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
{
List *puboids;
ListCell *lc;
MemoryContext oldcxt;
Oid schemaid;
List *ancestors = NIL;
Oid relid = RelationGetRelid(relation);
if (relation->rd_pubdesc)
{
pfree(relation->rd_pubdesc);
relation->rd_pubdesc = NULL;
}
/* Now save copy of the descriptor in the relcache entry. */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
relation->rd_pubdesc = palloc(sizeof(PublicationDesc));
memcpy(relation->rd_pubdesc, pubdesc, sizeof(PublicationDesc));
MemoryContextSwitchTo(oldcxt);
}
fp = AllocateFile(initfilename, PG_BINARY_R);
if (fp == NULL)
return false;
/*
* Read the index relcache entries from the file. Note we will not enter
* any of them into the cache if the read fails partway through; this
* helps to guard against broken init files.
*/
max_rels = 100;
rels = (Relation *) palloc(max_rels * sizeof(Relation));
num_rels = 0;
nailed_rels = nailed_indexes = 0;
/* check for correct magic number (compatible version) */
if (fread(&magic, 1, sizeof(magic), fp) != sizeof(magic))
goto read_failed;
if (magic != RELCACHE_INIT_FILEMAGIC)
goto read_failed;
/* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts);
rel->rd_att->tdrefcount = 1; /* mark as refcounted */
rel->rd_att->tdtypeid = relform->reltype ? relform->reltype : RECORDOID;
rel->rd_att->tdtypmod = -1; /* just to be sure */
/* next read all the attribute tuple form data entries */
has_not_null = false;
for (i = 0; i < relform->relnatts; i++)
{
Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i);
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
if (len != ATTRIBUTE_FIXED_PART_SIZE)
goto read_failed;
if (fread(attr, 1, len, fp) != len)
goto read_failed;
has_not_null |= attr->attnotnull;
populate_compact_attribute(rel->rd_att, i);
}
/* next read the access method specific field */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
if (len > 0)
{
rel->rd_options = palloc(len);
if (fread(rel->rd_options, 1, len, fp) != len)
goto read_failed;
if (len != VARSIZE(rel->rd_options))
goto read_failed; /* sanity check */
}
else
{
rel->rd_options = NULL;
}
/* mark not-null status */
if (has_not_null)
{
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
/*
* If it's an index, there's more to do. Note we explicitly ignore
* partitioned indexes here.
*/
if (rel->rd_rel->relkind == RELKIND_INDEX)
{
MemoryContext indexcxt;
Oid *opfamily;
Oid *opcintype;
RegProcedure *support;
int nsupport;
int16 *indoption;
Oid *indcollation;
/* Count nailed indexes to ensure we have 'em all */
if (rel->rd_isnailed)
nailed_indexes++;
/* read the pg_index tuple */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
/* Fix up internal pointers in the tuple -- see heap_copytuple */
rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
/*
* prepare index info context --- parameters should match
* RelationInitIndexAccessInfo
*/
indexcxt = AllocSetContextCreate(CacheMemoryContext, "index info",
ALLOCSET_SMALL_SIZES);
rel->rd_indexcxt = indexcxt;
MemoryContextCopyAndSetIdentifier(indexcxt,
RelationGetRelationName(rel));
/*
* Now we can fetch the index AM's API struct. (We can't store
* that in the init file, since it contains function pointers that
* might vary across server executions. Fortunately, it should be
* safe to call the amhandler even while bootstrapping indexes.)
*/
InitIndexAmRoutine(rel);
/* read the vector of opfamily OIDs */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
/* set up zeroed fmgr-info vector */
nsupport = relform->relnatts * rel->rd_indam->amsupport;
rel->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
else
{
/* Count nailed rels to ensure we have 'em all */
if (rel->rd_isnailed)
nailed_rels++;
/* Load table AM data */
if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE)
RelationInitTableAccessMethod(rel);
/*
* Rules and triggers are not saved (mainly because the internal
* format is complex and subject to change). They must be rebuilt if
* needed by RelationCacheInitializePhase3. This is not expected to
* be a big performance hit since few system catalogs have such. Ditto
* for RLS policy data, partition info, index expressions, predicates,
* exclusion info, and FDW info.
*/
rel->rd_rules = NULL;
rel->rd_rulescxt = NULL;
rel->trigdesc = NULL;
rel->rd_rsdesc = NULL;
rel->rd_partkey = NULL;
rel->rd_partkeycxt = NULL;
rel->rd_partdesc = NULL;
rel->rd_partdesc_nodetached = NULL;
rel->rd_partdesc_nodetached_xmin = InvalidTransactionId;
rel->rd_pdcxt = NULL;
rel->rd_pddcxt = NULL;
rel->rd_partcheck = NIL;
rel->rd_partcheckvalid = false;
rel->rd_partcheckcxt = NULL;
rel->rd_indexprs = NIL;
rel->rd_indpred = NIL;
rel->rd_exclops = NULL;
rel->rd_exclprocs = NULL;
rel->rd_exclstrats = NULL;
rel->rd_fdwroutine = NULL;
/*
* Recompute lock and physical addressing info. This is needed in
* case the pg_internal.init file was copied from some other database
* by CREATE DATABASE.
*/
RelationInitLockInfo(rel);
RelationInitPhysicalAddr(rel);
}
/*
* We reached the end of the init file without apparent problem. Did we
* get the right number of nailed items? This is a useful crosscheck in
* case the set of critical rels or indexes changes. However, that should
* not happen in a normally-running system, so let's bleat if it does.
*
* For the shared init file, we're called before client authentication is
* done, which means that elog(WARNING) will go only to the postmaster
* log, where it's easily missed. To ensure that developers notice bad
* values of NUM_CRITICAL_SHARED_RELS/NUM_CRITICAL_SHARED_INDEXES, we put
* an Assert(false) there.
*/
if (shared)
{
if (nailed_rels != NUM_CRITICAL_SHARED_RELS ||
nailed_indexes != NUM_CRITICAL_SHARED_INDEXES)
{
elog(WARNING, "found %d nailed shared rels and %d nailed shared indexes in init file, but expected %d and %d respectively",
nailed_rels, nailed_indexes,
NUM_CRITICAL_SHARED_RELS, NUM_CRITICAL_SHARED_INDEXES);
/* Make sure we get developers' attention about this */
Assert(false);
/* In production builds, recover by bootstrapping the relcache */
goto read_failed;
}
}
else
{
if (nailed_rels != NUM_CRITICAL_LOCAL_RELS ||
nailed_indexes != NUM_CRITICAL_LOCAL_INDEXES)
{
elog(WARNING, "found %d nailed rels and %d nailed indexes in init file, but expected %d and %d respectively",
nailed_rels, nailed_indexes,
NUM_CRITICAL_LOCAL_RELS, NUM_CRITICAL_LOCAL_INDEXES);
/* We don't need an Assert() in this case */
goto read_failed;
}
}
/*
* OK, all appears well.
*
* Now insert all the new relcache entries into the cache.
*/
for (relno = 0; relno < num_rels; relno++)
{
RelationCacheInsert(rels[relno], false);
}
/*
* init file is broken, so do it the hard way. We don't bother trying to
* free the clutter we just allocated; it's not in the relcache so it
* won't hurt.
*/
read_failed:
pfree(rels);
FreeFile(fp);
return false;
}
/*
* Write out a new initialization file with the current contents
* of the relcache (either shared rels or local rels, as indicated).
*/
static void
write_relcache_init_file(bool shared)
{
FILE *fp;
char tempfilename[MAXPGPATH];
char finalfilename[MAXPGPATH];
int magic;
HASH_SEQ_STATUS status;
RelIdCacheEnt *idhentry;
int i;
/*
* If we have already received any relcache inval events, there's no
* chance of succeeding so we may as well skip the whole thing.
*/
if (relcacheInvalsReceived != 0L)
return;
/*
* We must write a temporary file and rename it into place. Otherwise,
* another backend starting at about the same time might crash trying to
* read the partially-complete file.
*/
if (shared)
{
snprintf(tempfilename, sizeof(tempfilename), "global/%s.%d",
RELCACHE_INIT_FILENAME, MyProcPid);
snprintf(finalfilename, sizeof(finalfilename), "global/%s",
RELCACHE_INIT_FILENAME);
}
else
{
snprintf(tempfilename, sizeof(tempfilename), "%s/%s.%d",
DatabasePath, RELCACHE_INIT_FILENAME, MyProcPid);
snprintf(finalfilename, sizeof(finalfilename), "%s/%s",
DatabasePath, RELCACHE_INIT_FILENAME);
}
unlink(tempfilename); /* in case it exists w/wrong permissions */
fp = AllocateFile(tempfilename, PG_BINARY_W);
if (fp == NULL)
{
/*
* We used to consider this a fatal error, but we might as well
* continue with backend startup ...
*/
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not create relation-cache initialization file \"%s\": %m",
tempfilename),
errdetail("Continuing anyway, but there's something wrong.")));
return;
}
/*
* Write a magic number to serve as a file version identifier. We can
* change the magic number whenever the relcache layout changes.
*/
magic = RELCACHE_INIT_FILEMAGIC;
if (fwrite(&magic, 1, sizeof(magic), fp) != sizeof(magic))
ereport(FATAL,
errcode_for_file_access(),
errmsg_internal("could not write init file: %m"));
/*
* Write all the appropriate reldescs (in no particular order).
*/
hash_seq_init(&status, RelationIdCache);
/* ignore if not correct group */
if (relform->relisshared != shared)
continue;
/*
* Ignore if not supposed to be in init file. We can allow any shared
* relation that's been loaded so far to be in the shared init file,
* but unshared relations must be ones that should be in the local
* file per RelationIdIsInInitFile. (Note: if you want to change the
* criterion for rels to be kept in the init file, see also inval.c.
* The reason for filtering here is to be sure that we don't put
* anything into the local init file for which a relcache inval would
* not cause invalidation of that init file.)
*/
if (!shared && !RelationIdIsInInitFile(RelationGetRelid(rel)))
{
/* Nailed rels had better get stored. */
Assert(!rel->rd_isnailed);
continue;
}
/* first write the relcache entry proper */
write_item(rel, sizeof(RelationData), fp);
/* next write the relation tuple form */
write_item(relform, CLASS_TUPLE_SIZE, fp);
/* next, do all the attribute tuple form data entries */
for (i = 0; i < relform->relnatts; i++)
{
write_item(TupleDescAttr(rel->rd_att, i),
ATTRIBUTE_FIXED_PART_SIZE, fp);
}
/* next, do the access method specific field */
write_item(rel->rd_options,
(rel->rd_options ? VARSIZE(rel->rd_options) : 0),
fp);
/*
* If it's an index, there's more to do. Note we explicitly ignore
* partitioned indexes here.
*/
if (rel->rd_rel->relkind == RELKIND_INDEX)
{
/* write the pg_index tuple */
/* we assume this was created by heap_copytuple! */
write_item(rel->rd_indextuple,
HEAPTUPLESIZE + rel->rd_indextuple->t_len,
fp);
/* write the vector of opfamily OIDs */
write_item(rel->rd_opfamily,
relform->relnatts * sizeof(Oid),
fp);
/* write the vector of opcintype OIDs */
write_item(rel->rd_opcintype,
relform->relnatts * sizeof(Oid),
fp);
/* write the vector of support procedure OIDs */
write_item(rel->rd_support,
relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
fp);
/* write the vector of collation OIDs */
write_item(rel->rd_indcollation,
relform->relnatts * sizeof(Oid),
fp);
/* write the vector of indoption values */
write_item(rel->rd_indoption,
relform->relnatts * sizeof(int16),
fp);
Assert(rel->rd_opcoptions);
/* write the vector of opcoptions values */
for (i = 0; i < relform->relnatts; i++)
{
bytea *opt = rel->rd_opcoptions[i];
if (FreeFile(fp))
ereport(FATAL,
errcode_for_file_access(),
errmsg_internal("could not write init file: %m"));
/*
* Now we have to check whether the data we've so painstakingly
* accumulated is already obsolete due to someone else's just-committed
* catalog changes. If so, we just delete the temp file and leave it to
* the next backend to try again. (Our own relcache entries will be
* updated by SI message processing, but we can't be sure whether what we
* wrote out was up-to-date.)
*
* This mustn't run concurrently with the code that unlinks an init file
* and sends SI messages, so grab a serialization lock for the duration.
*/
LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);
/* Make sure we have seen all incoming SI messages */
AcceptInvalidationMessages();
/*
* If we have received any SI relcache invals since backend start, assume
* we may have written out-of-date data.
*/
if (relcacheInvalsReceived == 0L)
{
/*
* OK, rename the temp file to its final name, deleting any
* previously-existing init file.
*
* Note: a failure here is possible under Cygwin, if some other
* backend is holding open an unlinked-but-not-yet-gone init file. So
* treat this as a noncritical failure; just remove the useless temp
* file on failure.
*/
if (rename(tempfilename, finalfilename) < 0)
unlink(tempfilename);
}
else
{
/* Delete the already-obsolete temp file */
unlink(tempfilename);
}
LWLockRelease(RelCacheInitLock);
}
/* write a chunk of data preceded by its length */
static void
write_item(const void *data, Size len, FILE *fp)
{
if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
ereport(FATAL,
errcode_for_file_access(),
errmsg_internal("could not write init file: %m"));
if (len > 0 && fwrite(data, 1, len, fp) != len)
ereport(FATAL,
errcode_for_file_access(),
errmsg_internal("could not write init file: %m"));
}
/*
* Determine whether a given relation (identified by OID) is one of the ones
* 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. 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 == SharedSecLabelRelationId ||
relationId == TriggerRelidNameIndexId ||
relationId == DatabaseNameIndexId ||
relationId == SharedSecLabelObjectIndexId)
{
/*
* If this Assert fails, we don't need the applicable special case
* anymore.
*/
Assert(!RelationSupportsSysCache(relationId));
return true;
}
return RelationSupportsSysCache(relationId);
}
/*
* Invalidate (remove) the init file during commit of a transaction that
* changed one or more of the relation cache entries that are kept in the
* local init file.
*
* To be safe against concurrent inspection or rewriting of the init file,
* we must take RelCacheInitLock, then remove the old init file, then send
* the SI messages that include relcache inval for such relations, and then
* release RelCacheInitLock. This serializes the whole affair against
* write_relcache_init_file, so that we can be sure that any other process
* that's concurrently trying to create a new init file won't move an
* already-stale version into place after we unlink. Also, because we unlink
* before sending the SI messages, a backend that's currently starting cannot
* read the now-obsolete init file and then miss the SI messages that will
* force it to update its relcache entries. (This works because the backend
* startup sequence gets into the sinval array before trying to load the init
* file.)
*
* 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.
*/
void
RelationCacheInitFilePreInvalidate(void)
{
char localinitfname[MAXPGPATH];
char sharedinitfname[MAXPGPATH];
/*
* 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);
}
/*
* Remove the init files during postmaster startup.
*
* We used to keep the init files across restarts, but that is unsafe in PITR
* scenarios, and even in simple crash-recovery cases there are windows for
* the init files to become out-of-sync with the database. So now we just
* remove them during startup and expect the first backend launch to rebuild
* them. Of course, this has to happen in each database of the cluster.
*/
void
RelationCacheInitFileRemove(void)
{
const char *tblspcdir = PG_TBLSPC_DIR;
DIR *dir;
struct dirent *de;
char path[MAXPGPATH + sizeof(PG_TBLSPC_DIR) + sizeof(TABLESPACE_VERSION_DIRECTORY)];
/* Scan everything in the default tablespace */
RelationCacheInitFileRemoveInDir("base");
/* Scan the tablespace link directory to find non-default tablespaces */
dir = AllocateDir(tblspcdir);
while ((de = ReadDirExtended(dir, tblspcdir, LOG)) != NULL)
{
if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
{
/* Scan the tablespace dir for per-database dirs */
snprintf(path, sizeof(path), "%s/%s/%s",
tblspcdir, de->d_name, TABLESPACE_VERSION_DIRECTORY);
RelationCacheInitFileRemoveInDir(path);
}
}
FreeDir(dir);
}
/* Process one per-tablespace directory for RelationCacheInitFileRemove */
static void
RelationCacheInitFileRemoveInDir(const char *tblspcpath)
{
DIR *dir;
struct dirent *de;
char initfilename[MAXPGPATH * 2];
/* Scan the tablespace directory to find per-database directories */
dir = AllocateDir(tblspcpath);
while ((de = ReadDirExtended(dir, tblspcpath, LOG)) != NULL)
{
if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
{
/* 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, LOG);
}
}
FreeDir(dir);
}
static void
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)
ereport(elevel,
(errcode_for_file_access(),
errmsg("could not remove cache file \"%s\": %m",
initfilename)));
}
}
/*
* This reference has already been removed from the resource owner, so
* just decrement reference count without calling
* ResourceOwnerForgetRelationRef.
*/
Assert(rel->rd_refcnt > 0);
rel->rd_refcnt -= 1;
RelationCloseCleanup((Relation) res);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.655Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.