📄 relcache.c
字号:
rebuildFirstList = lcons(relation, rebuildFirstList); else rebuildFirstList = lappend(rebuildFirstList, relation); } else rebuildList = lcons(relation, rebuildList); } } /* * Now zap any remaining smgr cache entries. This must happen before we * start to rebuild entries, since that may involve catalog fetches which * will re-open catalog files. */ smgrcloseall(); /* Phase 2: rebuild the items found to need rebuild in phase 1 */ foreach(l, rebuildFirstList) { relation = (Relation) lfirst(l); RelationClearRelation(relation, true); } list_free(rebuildFirstList); foreach(l, rebuildList) { relation = (Relation) lfirst(l); RelationClearRelation(relation, true); } list_free(rebuildList);}/* * AtEOXact_RelationCache * * Clean up the relcache at main-transaction commit or abort. * * Note: this must be called *before* processing invalidation messages. * In the case of abort, we don't want to try to rebuild any invalidated * cache entries (since we can't safely do database accesses). Therefore * we must reset refcnts before handling pending invalidations. * * As of PostgreSQL 8.1, relcache refcnts should get released by the * ResourceOwner mechanism. This routine just does a debugging * cross-check that no pins remain. However, we also need to do special * cleanup when the current transaction created any relations or made use * of forced index lists. */voidAtEOXact_RelationCache(bool isCommit){ HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; /* * To speed up transaction exit, we want to avoid scanning the relcache * unless there is actually something for this routine to do. Other than * the debug-only Assert checks, most transactions don't create any work * for us to do here, so we keep a static flag that gets set if there is * anything to do. (Currently, this means either a relation is created in * the current xact, or an index list is forced.) For simplicity, the * flag remains set till end of top-level transaction, even though we * could clear it at subtransaction end in some cases. */ if (!need_eoxact_work#ifdef USE_ASSERT_CHECKING && !assert_enabled#endif ) return; hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) { Relation relation = idhentry->reldesc; /* * The relcache entry's ref count should be back to its normal * not-in-a-transaction state: 0 unless it's nailed in cache. * * In bootstrap mode, this is NOT true, so don't check it --- the * bootstrap code expects relations to stay open across start/commit * transaction calls. (That seems bogus, but it's not worth fixing.) */#ifdef USE_ASSERT_CHECKING if (!IsBootstrapProcessingMode()) { int expected_refcnt; expected_refcnt = relation->rd_isnailed ? 1 : 0; Assert(relation->rd_refcnt == expected_refcnt); }#endif /* * Is it a relation created in the current transaction? * * During commit, reset the flag to zero, since we are now out of the * creating transaction. During abort, simply delete the relcache * entry --- it isn't interesting any longer. (NOTE: if we have * forgotten the new-ness of a new relation due to a forced cache * flush, the entry will get deleted anyway by shared-cache-inval * processing of the aborted pg_class insertion.) */ if (relation->rd_createSubid != InvalidSubTransactionId) { if (isCommit) relation->rd_createSubid = InvalidSubTransactionId; else { RelationClearRelation(relation, false); continue; } } /* * Flush any temporary index list. */ if (relation->rd_indexvalid == 2) { list_free(relation->rd_indexlist); relation->rd_indexlist = NIL; relation->rd_oidindex = InvalidOid; relation->rd_indexvalid = 0; } } /* Once done with the transaction, we can reset need_eoxact_work */ need_eoxact_work = false;}/* * AtEOSubXact_RelationCache * * Clean up the relcache at sub-transaction commit or abort. * * Note: this must be called *before* processing invalidation messages. */voidAtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid){ HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; /* * Skip the relcache scan if nothing to do --- see notes for * AtEOXact_RelationCache. */ if (!need_eoxact_work) return; hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) { Relation relation = idhentry->reldesc; /* * Is it a relation created in the current subtransaction? * * During subcommit, mark it as belonging to the parent, instead. * During subabort, simply delete the relcache entry. */ if (relation->rd_createSubid == mySubid) { if (isCommit) relation->rd_createSubid = parentSubid; else { Assert(RelationHasReferenceCountZero(relation)); RelationClearRelation(relation, false); continue; } } /* * Flush any temporary index list. */ if (relation->rd_indexvalid == 2) { list_free(relation->rd_indexlist); relation->rd_indexlist = NIL; relation->rd_oidindex = InvalidOid; relation->rd_indexvalid = 0; } }}/* * RelationBuildLocalRelation * Build a relcache entry for an about-to-be-created relation, * and enter it into the relcache. */RelationRelationBuildLocalRelation(const char *relname, Oid relnamespace, TupleDesc tupDesc, Oid relid, Oid reltablespace, bool shared_relation){ Relation rel; MemoryContext oldcxt; int natts = tupDesc->natts; int i; bool has_not_null; bool nailit; AssertArg(natts >= 0); /* * check for creation of a rel that must be nailed in cache. * * XXX this list had better match RelationCacheInitialize's list. */ switch (relid) { case RelationRelationId: case AttributeRelationId: case ProcedureRelationId: case TypeRelationId: nailit = true; break; default: nailit = false; break; } /* * switch to the cache context to create the relcache entry. */ if (!CacheMemoryContext) CreateCacheMemoryContext(); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * allocate a new relation descriptor and fill in basic state fields. */ rel = (Relation) palloc0(sizeof(RelationData)); rel->rd_targblock = InvalidBlockNumber; /* 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(); /* must flag that we have rels created in this transaction */ need_eoxact_work = true; /* is it a temporary relation? */ rel->rd_istemp = isTempNamespace(relnamespace); /* * create a new tuple descriptor from the one passed in. We do this * partly to copy it into the cache context, and partly because the new * relation can't have any defaults or constraints yet; they have to be * added in later steps, because they require additions to multiple system * catalogs. We can copy attnotnull constraints here, however. */ rel->rd_att = CreateTupleDescCopy(tupDesc); has_not_null = false; for (i = 0; i < natts; i++) { rel->rd_att->attrs[i]->attnotnull = tupDesc->attrs[i]->attnotnull; has_not_null |= tupDesc->attrs[i]->attnotnull; } if (has_not_null) { TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr)); constr->has_not_null = true; rel->rd_att->constr = constr; } /* * initialize relation tuple form (caller may add/override data later) */ rel->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE); namestrcpy(&rel->rd_rel->relname, relname); rel->rd_rel->relnamespace = relnamespace; rel->rd_rel->relkind = RELKIND_UNCATALOGED; rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; rel->rd_rel->relnatts = natts; rel->rd_rel->reltype = InvalidOid; /* needed when bootstrapping: */ rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID; /* * Insert relation physical and logical identifiers (OIDs) into the right * places. Note that the physical ID (relfilenode) is initially the same * as the logical ID (OID). */ rel->rd_rel->relisshared = shared_relation; RelationGetRelid(rel) = relid; for (i = 0; i < natts; i++) rel->rd_att->attrs[i]->attrelid = relid; rel->rd_rel->relfilenode = relid; rel->rd_rel->reltablespace = reltablespace; RelationInitLockInfo(rel); /* see lmgr.c */ RelationInitPhysicalAddr(rel); /* * Okay to insert into the relcache hash tables. */ RelationCacheInsert(rel); /* * done building relcache entry. */ MemoryContextSwitchTo(oldcxt); /* It's fully valid */ rel->rd_isvalid = true; /* * Caller expects us to pin the returned entry. */ RelationIncrementReferenceCount(rel); return rel;}/* * RelationCacheInitialize * * This initializes the relation descriptor cache. At the time * that this is invoked, we can't do database access yet (mainly * because the transaction subsystem is not up), so we can't get * "real" info. However it's okay to read the pg_internal.init * cache file, if one is available. Otherwise we make phony * entries for the minimum set of nailed-in-cache relations. */#define INITRELCACHESIZE 400voidRelationCacheInitialize(void){ MemoryContext oldcxt; HASHCTL ctl; /* * switch to cache memory context */ if (!CacheMemoryContext) CreateCacheMemoryContext(); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * create hashtables that index the relcache */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(RelIdCacheEnt); ctl.hash = oid_hash; RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE, &ctl, HASH_ELEM | HASH_FUNCTION); /* * Try to load the relcache cache file. If successful, we're done for * now. Otherwise, initialize the cache with pre-made descriptors for the * critical "nailed-in" system catalogs. */ if (IsBootstrapProcessingMode() || !load_relcache_init_file()) { formrdesc("pg_class", PG_CLASS_RELTYPE_OID, true, Natts_pg_class, Desc_pg_class); formrdesc("pg_attribute", PG_ATTRIBUTE_RELTYPE_OID, false, Natts_pg_attribute, Desc_pg_attribute); formrdesc("pg_proc", PG_PROC_RELTYPE_OID, true, Natts_pg_proc, Desc_pg_proc); formrdesc("pg_type", PG_TYPE_RELTYPE_OID, true, Natts_pg_type, Desc_pg_type);#define NUM_CRITICAL_RELS 4 /* fix if you change list above */ } MemoryContextSwitchTo(oldcxt);}/* * RelationCacheInitializePhase2 * * This is called as soon as the catcache and transaction system * are functional. At this point we can actually read data from * the system catalogs. Update the relcache entries made during * RelationCacheInitialize, and make sure we have entries for the * critical system indexes. */voidRelationCacheInitializePhase2(void){ HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; if (IsBootstrapProcessingMode()) return; /* * If we didn't get the critical system indexes loaded into relcache, do * so now. These are critical because the catcache depends on them for * catcache fetches that are done during relcache load. Thus, we have an * infinite-recursion problem. We can break the recursion by doing * heapscans instead of indexscans at certain key spots. To avoid hobbling * performance, we only want to do that until we have the critical indexes * loaded into relcache. Thus, the flag criticalRelcachesBuilt is used to * decide whether to do heapscan or indexscan at the key spots, and we set * it true after we've loaded the critical indexes. * * The critical indexes are marked as "nailed in cache", partly to make it * easy for load_relcache_init_file to count them, but mainly because we * cannot flush and rebuild them once we've set criticalRelcachesBuilt to * true. (NOTE: perhaps it would be possible to reload them by * temporarily setting criticalRelcachesBuilt to false again. For now, * though, we just nail 'em in.) */ if (!criticalRelcachesBuilt) { Relation ird;#define LOAD_CRIT_INDEX(indexoid) \ do { \ ird = RelationBuildDesc((indexoid), NULL); \ ird->rd_isnailed = true; \ ird->rd_refcnt = 1; \ } while (0) LOAD_CRIT_INDEX(ClassOidIndexId); LOAD_CRIT_INDEX(AttributeRelidNumIndexId); LOAD_CRIT_INDEX(IndexRelidIndexI
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -