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 + -
显示快捷键?