📄 relcache.c
字号:
/* * first try and get a reldesc from the cache */ rd = RelationIdCacheGetRelation(relationId); if (RelationIsValid(rd)) return rd; /* * no reldesc in the cache, so have RelationBuildDesc() build one and add * it. */ rd = RelationBuildDesc(relationId, NULL); if (RelationIsValid(rd)) RelationIncrementReferenceCount(rd); return rd;}/* ---------------------------------------------------------------- * cache invalidation support routines * ---------------------------------------------------------------- *//* * RelationIncrementReferenceCount * Increments relation reference count. * * Note: bootstrap mode has its own weird ideas about relation refcount * behavior; we ought to fix it someday, but for now, just disable * reference count ownership tracking in bootstrap mode. */voidRelationIncrementReferenceCount(Relation rel){ ResourceOwnerEnlargeRelationRefs(CurrentResourceOwner); rel->rd_refcnt += 1; if (!IsBootstrapProcessingMode()) ResourceOwnerRememberRelationRef(CurrentResourceOwner, rel);}/* * RelationDecrementReferenceCount * Decrements relation reference count. */voidRelationDecrementReferenceCount(Relation rel){ Assert(rel->rd_refcnt > 0); rel->rd_refcnt -= 1; if (!IsBootstrapProcessingMode()) ResourceOwnerForgetRelationRef(CurrentResourceOwner, rel);}/* * RelationClose - close an open relation * * Actually, we just decrement the refcount. * * NOTE: if compiled with -DRELCACHE_FORCE_RELEASE then relcache entries * will be freed as soon as their refcount goes to zero. In combination * with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test * to catch references to already-released relcache entries. It slows * things down quite a bit, however. */voidRelationClose(Relation relation){ /* Note: no locking manipulations needed */ RelationDecrementReferenceCount(relation);#ifdef RELCACHE_FORCE_RELEASE if (RelationHasReferenceCountZero(relation) && relation->rd_createSubid == InvalidSubTransactionId) RelationClearRelation(relation, false);#endif}/* * RelationReloadClassinfo - reload the pg_class row (only) * * This function is used only for indexes. We currently allow only the * pg_class row of an existing index to change (to support changes of * owner, tablespace, or relfilenode), not its pg_index row or other * subsidiary index schema information. Therefore it's sufficient to do * this when we get an SI invalidation. Furthermore, there are cases * where it's necessary not to throw away the index information, especially * for "nailed" indexes which we are unable to rebuild on-the-fly. * * We can't necessarily reread the pg_class row right away; we might be * in a failed transaction when we receive the SI notification. If so, * RelationClearRelation just marks the entry as invalid by setting * rd_isvalid to false. This routine is called to fix the entry when it * is next needed. */static voidRelationReloadClassinfo(Relation relation){ bool indexOK; HeapTuple pg_class_tuple; Form_pg_class relp; /* Should be called only for invalidated indexes */ Assert(relation->rd_rel->relkind == RELKIND_INDEX && !relation->rd_isvalid); /* Should be closed at smgr level */ Assert(relation->rd_smgr == NULL); /* * Read the pg_class row * * Don't try to use an indexscan of pg_class_oid_index to reload the info * for pg_class_oid_index ... */ indexOK = (RelationGetRelid(relation) != ClassOidIndexId); pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), indexOK); if (!HeapTupleIsValid(pg_class_tuple)) elog(ERROR, "could not find pg_class tuple for index %u", RelationGetRelid(relation)); relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE); heap_freetuple(pg_class_tuple); /* We must recalculate physical address in case it changed */ RelationInitPhysicalAddr(relation); /* Make sure targblock is reset in case rel was truncated */ relation->rd_targblock = InvalidBlockNumber; /* Okay, now it's valid again */ relation->rd_isvalid = true;}/* * RelationClearRelation * * Physically blow away a relation cache entry, or reset it and rebuild * it from scratch (that is, from catalog entries). The latter path is * usually used when we are notified of a change to an open relation * (one with refcount > 0). However, this routine just does whichever * it's told to do; callers must determine which they want. */static voidRelationClearRelation(Relation relation, bool rebuild){ Oid old_reltype = relation->rd_rel->reltype; MemoryContext oldcxt; /* * Make sure smgr and lower levels close the relation's files, if they * weren't closed already. If the relation is not getting deleted, the * next smgr access should reopen the files automatically. This ensures * that the low-level file access state is updated after, say, a vacuum * truncation. */ RelationCloseSmgr(relation); /* * Never, never ever blow away a nailed-in system relation, because we'd * be unable to recover. However, we must reset rd_targblock, in case we * got called because of a relation cache flush that was triggered by * VACUUM. * * If it's a nailed index, then we need to re-read the pg_class row to see * if its relfilenode changed. We can't necessarily do that here, because * we might be in a failed transaction. We assume it's okay to do it if * there are open references to the relcache entry (cf notes for * AtEOXact_RelationCache). Otherwise just mark the entry as possibly * invalid, and it'll be fixed when next opened. */ if (relation->rd_isnailed) { relation->rd_targblock = InvalidBlockNumber; if (relation->rd_rel->relkind == RELKIND_INDEX) { relation->rd_isvalid = false; /* needs to be revalidated */ if (relation->rd_refcnt > 1) RelationReloadClassinfo(relation); } return; } /* * Even non-system indexes should not be blown away if they are open and * have valid index support information. This avoids problems with active * use of the index support information. As with nailed indexes, we * re-read the pg_class row to handle possible physical relocation of * the index. */ if (relation->rd_rel->relkind == RELKIND_INDEX && relation->rd_refcnt > 0 && relation->rd_indexcxt != NULL) { relation->rd_isvalid = false; /* needs to be revalidated */ RelationReloadClassinfo(relation); return; } /* * Remove relation from hash tables * * Note: we might be reinserting it momentarily, but we must not have it * visible in the hash tables until it's valid again, so don't try to * optimize this away... */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); RelationCacheDelete(relation); MemoryContextSwitchTo(oldcxt); /* Clear out catcache's entries for this relation */ CatalogCacheFlushRelation(RelationGetRelid(relation)); /* * Free all the subsidiary data structures of the relcache entry. We * cannot free rd_att if we are trying to rebuild the entry, however, * because pointers to it may be cached in various places. The rule * manager might also have pointers into the rewrite rules. So to begin * with, we can only get rid of these fields: */ FreeTriggerDesc(relation->trigdesc); if (relation->rd_indextuple) pfree(relation->rd_indextuple); if (relation->rd_am) pfree(relation->rd_am); if (relation->rd_rel) pfree(relation->rd_rel); list_free(relation->rd_indexlist); if (relation->rd_indexcxt) MemoryContextDelete(relation->rd_indexcxt); /* * If we're really done with the relcache entry, blow it away. But if * someone is still using it, reconstruct the whole deal without moving * the physical RelationData record (so that the someone's pointer is * still valid). */ if (!rebuild) { /* ok to zap remaining substructure */ flush_rowtype_cache(old_reltype); FreeTupleDesc(relation->rd_att); if (relation->rd_rulescxt) MemoryContextDelete(relation->rd_rulescxt); pfree(relation); } else { /* * When rebuilding an open relcache entry, must preserve ref count and * rd_createSubid state. Also attempt to preserve the tupledesc and * rewrite-rule substructures in place. * * Note that this process does not touch CurrentResourceOwner; which * is good because whatever ref counts the entry may have do not * necessarily belong to that resource owner. */ Oid save_relid = RelationGetRelid(relation); int old_refcnt = relation->rd_refcnt; SubTransactionId old_createSubid = relation->rd_createSubid; TupleDesc old_att = relation->rd_att; RuleLock *old_rules = relation->rd_rules; MemoryContext old_rulescxt = relation->rd_rulescxt; if (RelationBuildDesc(save_relid, relation) != relation) { /* Should only get here if relation was deleted */ flush_rowtype_cache(old_reltype); FreeTupleDesc(old_att); if (old_rulescxt) MemoryContextDelete(old_rulescxt); pfree(relation); elog(ERROR, "relation %u deleted while still in use", save_relid); } relation->rd_refcnt = old_refcnt; relation->rd_createSubid = old_createSubid; if (equalTupleDescs(old_att, relation->rd_att)) { /* needn't flush typcache here */ FreeTupleDesc(relation->rd_att); relation->rd_att = old_att; } else { flush_rowtype_cache(old_reltype); FreeTupleDesc(old_att); } if (equalRuleLocks(old_rules, relation->rd_rules)) { if (relation->rd_rulescxt) MemoryContextDelete(relation->rd_rulescxt); relation->rd_rules = old_rules; relation->rd_rulescxt = old_rulescxt; } else { if (old_rulescxt) MemoryContextDelete(old_rulescxt); } }}/* * RelationFlushRelation * * Rebuild the relation if it is open (refcount > 0), else blow it away. */static voidRelationFlushRelation(Relation relation){ bool rebuild; if (relation->rd_createSubid != InvalidSubTransactionId) { /* * New relcache entries are always rebuilt, not flushed; else we'd * forget the "new" status of the relation, which is a useful * optimization to have. */ rebuild = true; } else { /* * Pre-existing rels can be dropped from the relcache if not open. */ rebuild = !RelationHasReferenceCountZero(relation); } RelationClearRelation(relation, rebuild);}/* * RelationForgetRelation - unconditionally remove a relcache entry * * External interface for destroying a relcache entry when we * drop the relation. */voidRelationForgetRelation(Oid rid){ Relation relation; RelationIdCacheLookup(rid, relation); if (!PointerIsValid(relation)) return; /* not in cache, nothing to do */ if (!RelationHasReferenceCountZero(relation)) elog(ERROR, "relation %u is still open", rid); /* Unconditionally destroy the relcache entry */ RelationClearRelation(relation, false);}/* * RelationCacheInvalidateEntry * * This routine is invoked for SI cache flush messages. * * Any relcache entry matching the relid must be flushed. (Note: caller has * already determined that the relid belongs to our database or is a shared * relation.) * * We used to skip local relations, on the grounds that they could * not be targets of cross-backend SI update messages; but it seems * safer to process them, so that our *own* SI update messages will * have the same effects during CommandCounterIncrement for both * local and nonlocal relations. */voidRelationCacheInvalidateEntry(Oid relationId){ Relation relation; RelationIdCacheLookup(relationId, relation); if (PointerIsValid(relation)) { relcacheInvalsReceived++; RelationFlushRelation(relation); }}/* * RelationCacheInvalidate * Blow away cached relation descriptors that have zero reference counts, * and rebuild those with positive reference counts. Also reset the smgr * relation cache. * * This is currently used only to recover from SI message buffer overflow, * so we do not touch new-in-transaction relations; they cannot be targets * of cross-backend SI updates (and our own updates now go through a * separate linked list that isn't limited by the SI message buffer size). * * We do this in two phases: the first pass deletes deletable items, and * the second one rebuilds the rebuildable items. This is essential for * safety, because hash_seq_search only copes with concurrent deletion of * the element it is currently visiting. If a second SI overflow were to * occur while we are walking the table, resulting in recursive entry to * this routine, we could crash because the inner invocation blows away * the entry next to be visited by the outer scan. But this way is OK, * because (a) during the first pass we won't process any more SI messages, * so hash_seq_search will complete safely; (b) during the second pass we * only hold onto pointers to nondeletable entries. * * The two-phase approach also makes it easy to ensure that we process * nailed-in-cache indexes before other nondeletable items, and that we * process pg_class_oid_index first of all. In scenarios where a nailed * index has been given a new relfilenode, we have to detect that update * before the nailed index is used in reloading any other relcache entry. */voidRelationCacheInvalidate(void){ HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; Relation relation; List *rebuildFirstList = NIL; List *rebuildList = NIL; ListCell *l; /* Phase 1 */ hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) { relation = idhentry->reldesc; /* Must close all smgr references to avoid leaving dangling ptrs */ RelationCloseSmgr(relation); /* Ignore new relations, since they are never SI targets */ if (relation->rd_createSubid != InvalidSubTransactionId) continue; relcacheInvalsReceived++; if (RelationHasReferenceCountZero(relation)) { /* Delete this entry immediately */ Assert(!relation->rd_isnailed); RelationClearRelation(relation, false); } else { /* * Add this entry to list of stuff to rebuild in second pass. * pg_class_oid_index goes on the front of rebuildFirstList, other * nailed indexes on the back, and everything else into * rebuildList (in no particular order). */ if (relation->rd_isnailed && relation->rd_rel->relkind == RELKIND_INDEX) { if (RelationGetRelid(relation) == ClassOidIndexId)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -