relcache.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,282 行 · 第 1/5 页
C
2,282 行
* When rebuilding an open relcache entry, must preserve ref count * and rd_isnew flag. Also attempt to preserve the tupledesc and * rewrite-rule substructures in place. */ int old_refcnt = relation->rd_refcnt; bool old_isnew = relation->rd_isnew; TupleDesc old_att = relation->rd_att; RuleLock *old_rules = relation->rd_rules; MemoryContext old_rulescxt = relation->rd_rulescxt; RelationBuildDescInfo buildinfo; buildinfo.infotype = INFO_RELID; buildinfo.i.info_id = RelationGetRelid(relation); if (RelationBuildDesc(buildinfo, relation) != relation) { /* Should only get here if relation was deleted */ FreeTupleDesc(old_att); if (old_rulescxt) MemoryContextDelete(old_rulescxt); pfree(relation); elog(ERROR, "relation %u deleted while still in use", buildinfo.i.info_id); } RelationSetReferenceCount(relation, old_refcnt); relation->rd_isnew = old_isnew; if (equalTupleDescs(old_att, relation->rd_att)) { FreeTupleDesc(relation->rd_att); relation->rd_att = old_att; } else 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); } /* * Update rd_nblocks. This is kind of expensive, but I think we * must do it in case relation has been truncated... we definitely * must do it if the rel is new or temp, since * RelationGetNumberOfBlocks will subsequently assume that the * block count is correct. */ RelationUpdateNumberOfBlocks(relation); }}/* * RelationFlushRelation * * Rebuild the relation if it is open (refcount > 0), else blow it away. */static voidRelationFlushRelation(Relation relation){ bool rebuild; if (relation->rd_isnew) { /* * 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);}/* * RelationIdInvalidateRelationCacheByRelationId * * This routine is invoked for SI cache flush messages. * * 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. */voidRelationIdInvalidateRelationCacheByRelationId(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. * * 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; List *l; /* Phase 1 */ hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) { relation = idhentry->reldesc; /* Ignore new relations, since they are never SI targets */ if (relation->rd_isnew) 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 (strcmp(RelationGetRelationName(relation), ClassOidIndex) == 0) rebuildFirstList = lcons(relation, rebuildFirstList); else rebuildFirstList = lappend(rebuildFirstList, relation); } else rebuildList = lcons(relation, rebuildList); } } rebuildList = nconc(rebuildFirstList, rebuildList); /* Phase 2: rebuild the items found to need rebuild in phase 1 */ foreach(l, rebuildList) { relation = (Relation) lfirst(l); RelationClearRelation(relation, true); } freeList(rebuildList);}/* * AtEOXact_RelationCache * * Clean up the relcache at 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. */voidAtEOXact_RelationCache(bool commit){ HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; hash_seq_init(&status, RelationIdCache); while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) { Relation relation = idhentry->reldesc; int expected_refcnt; /* * Is it a relation created in the current transaction? * * During commit, reset the flag to false, 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 isnew state 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_isnew) { if (commit) relation->rd_isnew = false; else { RelationClearRelation(relation, false); continue; } } /* * During transaction abort, we must also reset relcache entry ref * counts to their normal not-in-a-transaction state. A ref count * may be too high because some routine was exited by ereport() * between incrementing and decrementing the count. * * During commit, we should not have to do this, but it's still * useful to check that the counts are correct to catch missed * relcache closes. * * In bootstrap mode, do NOT reset the refcnt nor complain that it's * nonzero --- the bootstrap code expects relations to stay open * across start/commit transaction calls. (That seems bogus, but * it's not worth fixing.) */ expected_refcnt = relation->rd_isnailed ? 1 : 0; if (commit) { if (relation->rd_refcnt != expected_refcnt && !IsBootstrapProcessingMode()) { elog(WARNING, "relcache reference leak: relation \"%s\" has refcnt %d instead of %d", RelationGetRelationName(relation), relation->rd_refcnt, expected_refcnt); RelationSetReferenceCount(relation, expected_refcnt); } } else { /* abort case, just reset it quietly */ RelationSetReferenceCount(relation, expected_refcnt); } /* * Flush any temporary index list. */ if (relation->rd_indexvalid == 2) { freeList(relation->rd_indexlist); relation->rd_indexlist = NIL; 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 dbid, RelFileNode rnode, bool nailit){ Relation rel; MemoryContext oldcxt; int natts = tupDesc->natts; int i; bool has_not_null; AssertArg(natts >= 0); /* * 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_fd = -1; RelationSetReferenceCount(rel, 1); /* it's being created in this transaction */ rel->rd_isnew = true; /* is it a temporary relation? */ rel->rd_istemp = isTempNamespace(relnamespace); /* * nail the reldesc if this is a bootstrap create reln and we may need * it in the cache later on in the bootstrap process so we don't ever * want it kicked out. e.g. pg_attribute!!! */ if (nailit) rel->rd_isnailed = 1; /* * 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; /* * Insert relation physical and logical identifiers (OIDs) into the * right places. */ rel->rd_rel->relisshared = (dbid == InvalidOid); RelationGetRelid(rel) = relid; for (i = 0; i < natts; i++) rel->rd_att->attrs[i]->attrelid = relid; rel->rd_node = rnode; rel->rd_rel->relfilenode = rnode.relNode; RelationInitLockInfo(rel); /* see lmgr.c */ /* * Okay to insert into the relcache hash tables. */ RelationCacheInsert(rel); /* * done building relcache entry. */ MemoryContextSwitchTo(oldcxt); 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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?