relcache.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,134 行 · 第 1/5 页

C
2,134
字号
	relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,												sizeof(TupleConstr));	constr->has_not_null = false;	/*	 * Form a scan key that selects only user attributes (attnum > 0).	 * (Eliminating system attribute rows at the index level is lots faster	 * than fetching them.)	 */	ScanKeyInit(&skey[0],				Anum_pg_attribute_attrelid,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(RelationGetRelid(relation)));	ScanKeyInit(&skey[1],				Anum_pg_attribute_attnum,				BTGreaterStrategyNumber, F_INT2GT,				Int16GetDatum(0));	/*	 * Open pg_attribute and begin a scan.	Force heap scan if we haven't yet	 * built the critical relcache entries (this includes initdb and startup	 * without a pg_internal.init file).	 */	pg_attribute_desc = heap_open(AttributeRelationId, AccessShareLock);	pg_attribute_scan = systable_beginscan(pg_attribute_desc,										   AttributeRelidNumIndexId,										   criticalRelcachesBuilt,										   SnapshotNow,										   2, skey);	/*	 * add attribute data to relation->rd_att	 */	need = relation->rd_rel->relnatts;	while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan)))	{		Form_pg_attribute attp;		attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);		if (attp->attnum <= 0 ||			attp->attnum > relation->rd_rel->relnatts)			elog(ERROR, "invalid attribute number %d for %s",				 attp->attnum, RelationGetRelationName(relation));		memcpy(relation->rd_att->attrs[attp->attnum - 1],			   attp,			   ATTRIBUTE_TUPLE_SIZE);		/* Update constraint/default info */		if (attp->attnotnull)			constr->has_not_null = true;		if (attp->atthasdef)		{			if (attrdef == NULL)				attrdef = (AttrDefault *)					MemoryContextAllocZero(CacheMemoryContext,										   relation->rd_rel->relnatts *										   sizeof(AttrDefault));			attrdef[ndef].adnum = attp->attnum;			attrdef[ndef].adbin = NULL;			ndef++;		}		need--;		if (need == 0)			break;	}	/*	 * end the scan and close the attribute relation	 */	systable_endscan(pg_attribute_scan);	heap_close(pg_attribute_desc, AccessShareLock);	if (need != 0)		elog(ERROR, "catalog is missing %d attribute(s) for relid %u",			 need, RelationGetRelid(relation));	/*	 * The attcacheoff values we read from pg_attribute should all be -1	 * ("unknown").  Verify this if assert checking is on.	They will be	 * computed when and if needed during tuple access.	 */#ifdef USE_ASSERT_CHECKING	{		int			i;		for (i = 0; i < relation->rd_rel->relnatts; i++)			Assert(relation->rd_att->attrs[i]->attcacheoff == -1);	}#endif	/*	 * However, we can easily set the attcacheoff value for the first	 * attribute: it must be zero.	This eliminates the need for special cases	 * for attnum=1 that used to exist in fastgetattr() and index_getattr().	 */	if (relation->rd_rel->relnatts > 0)		relation->rd_att->attrs[0]->attcacheoff = 0;	/*	 * Set up constraint/default info	 */	if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks)	{		relation->rd_att->constr = constr;		if (ndef > 0)			/* DEFAULTs */		{			if (ndef < relation->rd_rel->relnatts)				constr->defval = (AttrDefault *)					repalloc(attrdef, ndef * sizeof(AttrDefault));			else				constr->defval = attrdef;			constr->num_defval = ndef;			AttrDefaultFetch(relation);		}		else			constr->num_defval = 0;		if (relation->rd_rel->relchecks > 0)	/* CHECKs */		{			constr->num_check = relation->rd_rel->relchecks;			constr->check = (ConstrCheck *)				MemoryContextAllocZero(CacheMemoryContext,									constr->num_check * sizeof(ConstrCheck));			CheckConstraintFetch(relation);		}		else			constr->num_check = 0;	}	else	{		pfree(constr);		relation->rd_att->constr = NULL;	}}/* *		RelationBuildRuleLock * *		Form the relation's rewrite rules from information in *		the pg_rewrite system catalog. * * Note: The rule parsetrees are potentially very complex node structures. * To allow these trees to be freed when the relcache entry is flushed, * we make a private memory context to hold the RuleLock information for * each relcache entry that has associated rules.  The context is used * just for rule info, not for any other subsidiary data of the relcache * entry, because that keeps the update logic in RelationClearRelation() * manageable.	The other subsidiary data structures are simple enough * to be easy to free explicitly, anyway. */static voidRelationBuildRuleLock(Relation relation){	MemoryContext rulescxt;	MemoryContext oldcxt;	HeapTuple	rewrite_tuple;	Relation	rewrite_desc;	TupleDesc	rewrite_tupdesc;	SysScanDesc rewrite_scan;	ScanKeyData key;	RuleLock   *rulelock;	int			numlocks;	RewriteRule **rules;	int			maxlocks;	/*	 * Make the private context.  Parameters are set on the assumption that	 * it'll probably not contain much data.	 */	rulescxt = AllocSetContextCreate(CacheMemoryContext,									 RelationGetRelationName(relation),									 ALLOCSET_SMALL_MINSIZE,									 ALLOCSET_SMALL_INITSIZE,									 ALLOCSET_SMALL_MAXSIZE);	relation->rd_rulescxt = rulescxt;	/*	 * allocate an array to hold the rewrite rules (the array is extended if	 * necessary)	 */	maxlocks = 4;	rules = (RewriteRule **)		MemoryContextAlloc(rulescxt, sizeof(RewriteRule *) * maxlocks);	numlocks = 0;	/*	 * form a scan key	 */	ScanKeyInit(&key,				Anum_pg_rewrite_ev_class,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(RelationGetRelid(relation)));	/*	 * open pg_rewrite and begin a scan	 *	 * Note: since we scan the rules using RewriteRelRulenameIndexId, we will	 * be reading the rules in name order, except possibly during	 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn	 * ensures that rules will be fired in name order.	 */	rewrite_desc = heap_open(RewriteRelationId, AccessShareLock);	rewrite_tupdesc = RelationGetDescr(rewrite_desc);	rewrite_scan = systable_beginscan(rewrite_desc,									  RewriteRelRulenameIndexId,									  true, SnapshotNow,									  1, &key);	while (HeapTupleIsValid(rewrite_tuple = systable_getnext(rewrite_scan)))	{		Form_pg_rewrite rewrite_form = (Form_pg_rewrite) GETSTRUCT(rewrite_tuple);		bool		isnull;		Datum		rule_datum;		text	   *rule_text;		char	   *rule_str;		RewriteRule *rule;		rule = (RewriteRule *) MemoryContextAlloc(rulescxt,												  sizeof(RewriteRule));		rule->ruleId = HeapTupleGetOid(rewrite_tuple);		rule->event = rewrite_form->ev_type - '0';		rule->attrno = rewrite_form->ev_attr;		rule->enabled = rewrite_form->ev_enabled;		rule->isInstead = rewrite_form->is_instead;		/*		 * Must use heap_getattr to fetch ev_action and ev_qual.  Also, the		 * rule strings are often large enough to be toasted.  To avoid		 * leaking memory in the caller's context, do the detoasting here so		 * we can free the detoasted version.		 */		rule_datum = heap_getattr(rewrite_tuple,								  Anum_pg_rewrite_ev_action,								  rewrite_tupdesc,								  &isnull);		Assert(!isnull);		rule_text = DatumGetTextP(rule_datum);		rule_str = DatumGetCString(DirectFunctionCall1(textout,												PointerGetDatum(rule_text)));		oldcxt = MemoryContextSwitchTo(rulescxt);		rule->actions = (List *) stringToNode(rule_str);		MemoryContextSwitchTo(oldcxt);		pfree(rule_str);		if ((Pointer) rule_text != DatumGetPointer(rule_datum))			pfree(rule_text);		rule_datum = heap_getattr(rewrite_tuple,								  Anum_pg_rewrite_ev_qual,								  rewrite_tupdesc,								  &isnull);		Assert(!isnull);		rule_text = DatumGetTextP(rule_datum);		rule_str = DatumGetCString(DirectFunctionCall1(textout,												PointerGetDatum(rule_text)));		oldcxt = MemoryContextSwitchTo(rulescxt);		rule->qual = (Node *) stringToNode(rule_str);		MemoryContextSwitchTo(oldcxt);		pfree(rule_str);		if ((Pointer) rule_text != DatumGetPointer(rule_datum))			pfree(rule_text);		/*		 * We want the rule's table references to be checked as though by the		 * table owner, not the user referencing the rule.	Therefore, scan		 * through the rule's actions and set the checkAsUser field on all		 * rtable entries.	We have to look at the qual as well, in case it		 * contains sublinks.		 *		 * The reason for doing this when the rule is loaded, rather than when		 * it is stored, is that otherwise ALTER TABLE OWNER would have to		 * grovel through stored rules to update checkAsUser fields. Scanning		 * the rule tree during load is relatively cheap (compared to		 * constructing it in the first place), so we do it here.		 */		setRuleCheckAsUser((Node *) rule->actions, relation->rd_rel->relowner);		setRuleCheckAsUser(rule->qual, relation->rd_rel->relowner);		if (numlocks >= maxlocks)		{			maxlocks *= 2;			rules = (RewriteRule **)				repalloc(rules, sizeof(RewriteRule *) * maxlocks);		}		rules[numlocks++] = rule;	}	/*	 * end the scan and close the attribute relation	 */	systable_endscan(rewrite_scan);	heap_close(rewrite_desc, AccessShareLock);	/*	 * form a RuleLock and insert into relation	 */	rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));	rulelock->numLocks = numlocks;	rulelock->rules = rules;	relation->rd_rules = rulelock;}/* *		equalRuleLocks * *		Determine whether two RuleLocks are equivalent * *		Probably this should be in the rules code someplace... */static boolequalRuleLocks(RuleLock *rlock1, RuleLock *rlock2){	int			i;	/*	 * As of 7.3 we assume the rule ordering is repeatable, because	 * RelationBuildRuleLock should read 'em in a consistent order.  So just	 * compare corresponding slots.	 */	if (rlock1 != NULL)	{		if (rlock2 == NULL)			return false;		if (rlock1->numLocks != rlock2->numLocks)			return false;		for (i = 0; i < rlock1->numLocks; i++)		{			RewriteRule *rule1 = rlock1->rules[i];			RewriteRule *rule2 = rlock2->rules[i];			if (rule1->ruleId != rule2->ruleId)				return false;			if (rule1->event != rule2->event)				return false;			if (rule1->attrno != rule2->attrno)				return false;			if (rule1->isInstead != rule2->isInstead)				return false;			if (!equal(rule1->qual, rule2->qual))				return false;			if (!equal(rule1->actions, rule2->actions))				return false;		}	}	else if (rlock2 != NULL)		return false;	return true;}/* *		RelationBuildDesc * *		Build a relation descriptor --- either a new one, or by *		recycling the given old relation object.  The latter case *		supports rebuilding a relcache entry without invalidating *		pointers to it.  The caller must hold at least *		AccessShareLock on the target relid. * *		Returns NULL if no pg_class row could be found for the given relid *		(suggesting we are trying to access a just-deleted relation). *		Any other error is reported via elog. */static RelationRelationBuildDesc(Oid targetRelId, Relation oldrelation){	Relation	relation;	Oid			relid;	HeapTuple	pg_class_tuple;	Form_pg_class relp;	MemoryContext oldcxt;	/*	 * find the tuple in pg_class corresponding to the given relation id	 */	pg_class_tuple = ScanPgRelation(targetRelId, true);	/*	 * if no such tuple exists, return NULL	 */	if (!HeapTupleIsValid(pg_class_tuple))		return NULL;	/*	 * get information from the pg_class_tuple	 */	relid = HeapTupleGetOid(pg_class_tuple);	relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);	/*	 * allocate storage for the relation descriptor, and copy pg_class_tuple	 * to relation->rd_rel.	 */	relation = AllocateRelationDesc(oldrelation, relp);	/*	 * initialize the relation's relation id (relation->rd_id)	 */	RelationGetRelid(relation) = relid;	/*	 * normal relations are not nailed into the cache; nor can a pre-existing	 * relation be new.  It could be temp though.  (Actually, it could be new	 * too, but it's okay to forget that fact if forced to flush the entry.)	 */	relation->rd_refcnt = 0;	relation->rd_isnailed = false;	relation->rd_createSubid = InvalidSubTransactionId;	relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;	relation->rd_istemp = isTempOrToastNamespace(relation->rd_rel->relnamespace);	/*	 * initialize the tuple descriptor (relation->rd_att).	 */	RelationBuildTupleDesc(relation);	/*	 * Fetch rules and triggers that affect this relation

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?