rewritedefine.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 588 行 · 第 1/2 页

C
588
字号
			if (resdom->resjunk)				continue;			i++;			if (i > event_relation->rd_att->natts)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("SELECT rule's target list has too many entries")));			attr = event_relation->rd_att->attrs[i - 1];			attname = NameStr(attr->attname);			/*			 * Disallow dropped columns in the relation.  This won't			 * happen in the cases we actually care about (namely creating			 * a view via CREATE TABLE then CREATE RULE).  Trying to cope			 * with it is much more trouble than it's worth, because we'd			 * have to modify the rule to insert dummy NULLs at the right			 * positions.			 */			if (attr->attisdropped)				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("cannot convert relation containing dropped columns to view")));			if (strcmp(resdom->resname, attname) != 0)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));			if (attr->atttypid != resdom->restype)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("SELECT rule's target entry %d has different type from column \"%s\"", i, attname)));			/*			 * Allow typmods to be different only if one of them is -1,			 * ie, "unspecified".  This is necessary for cases like			 * "numeric", where the table will have a filled-in default			 * length but the select rule's expression will probably have			 * typmod = -1.			 */			if (attr->atttypmod != resdom->restypmod &&				attr->atttypmod != -1 && resdom->restypmod != -1)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("SELECT rule's target entry %d has different size from column \"%s\"", i, attname)));		}		if (i != event_relation->rd_att->natts)			ereport(ERROR,					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),			   errmsg("SELECT rule's target list has too few entries")));		/*		 * ... there must not be another ON SELECT rule already ...		 */		if (!replace && event_relation->rd_rules != NULL)		{			for (i = 0; i < event_relation->rd_rules->numLocks; i++)			{				RewriteRule *rule;				rule = event_relation->rd_rules->rules[i];				if (rule->event == CMD_SELECT)					ereport(ERROR,					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),					   errmsg("\"%s\" is already a view",							  RelationGetRelationName(event_relation))));			}		}		/*		 * ... and finally the rule must be named _RETURN.		 */		if (strcmp(stmt->rulename, ViewSelectRuleName) != 0)		{			/*			 * In versions before 7.3, the expected name was _RETviewname.			 * For backwards compatibility with old pg_dump output, accept			 * that and silently change it to _RETURN.	Since this is just			 * a quick backwards-compatibility hack, limit the number of			 * characters checked to a few less than NAMEDATALEN; this			 * saves having to worry about where a multibyte character			 * might have gotten truncated.			 */			if (strncmp(stmt->rulename, "_RET", 4) != 0 ||				strncmp(stmt->rulename + 4, event_obj->relname,						NAMEDATALEN - 4 - 4) != 0)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),					  errmsg("view rule for \"%s\" must be named \"%s\"",							 event_obj->relname, ViewSelectRuleName)));			stmt->rulename = pstrdup(ViewSelectRuleName);		}		/*		 * Are we converting a relation to a view?		 *		 * If so, check that the relation is empty because the storage for		 * the relation is going to be deleted.  Also insist that the rel		 * not have any triggers, indexes, or child tables.		 */		if (event_relation->rd_rel->relkind != RELKIND_VIEW)		{			HeapScanDesc scanDesc;			scanDesc = heap_beginscan(event_relation, SnapshotNow, 0, NULL);			if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)				ereport(ERROR,					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),				errmsg("could not convert table \"%s\" to a view because it is not empty",					   event_obj->relname)));			heap_endscan(scanDesc);			if (event_relation->rd_rel->reltriggers != 0)				ereport(ERROR,						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),						 errmsg("could not convert table \"%s\" to a view because it has triggers",								event_obj->relname),						 errhint("In particular, the table may not be involved in any foreign key relationships.")));			if (event_relation->rd_rel->relhasindex)				ereport(ERROR,						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),						 errmsg("could not convert table \"%s\" to a view because it has indexes",								event_obj->relname)));			if (event_relation->rd_rel->relhassubclass)				ereport(ERROR,						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),						 errmsg("could not convert table \"%s\" to a view because it has child tables",								event_obj->relname)));			RelisBecomingView = true;		}	}	/*	 * This rule is allowed - prepare to install it.	 */	event_attno = -1;	event_attype = InvalidOid;	/*	 * We want the rule's table references to be checked as though by the	 * rule owner, not the user referencing the rule.  Therefore, scan	 * through the rule's rtables and set the checkAsUser field on all	 * rtable entries.	 */	foreach(l, action)	{		query = (Query *) lfirst(l);		setRuleCheckAsUser(query, GetUserId());	}	/* discard rule if it's null action and not INSTEAD; it's a no-op */	if (action != NIL || is_instead)	{		ruleId = InsertRule(stmt->rulename,							event_type,							ev_relid,							event_attno,							is_instead,							event_qual,							action,							replace);		/*		 * Set pg_class 'relhasrules' field TRUE for event relation. If		 * appropriate, also modify the 'relkind' field to show that the		 * relation is now a view.		 *		 * Important side effect: an SI notice is broadcast to force all		 * backends (including me!) to update relcache entries with the		 * new rule.		 */		SetRelationRuleStatus(ev_relid, true, RelisBecomingView);	}	/*	 * IF the relation is becoming a view, delete the storage files	 * associated with it.	NB: we had better have AccessExclusiveLock to	 * do this ...	 *	 * XXX what about getting rid of its TOAST table?  For now, we don't.	 */	if (RelisBecomingView)		smgrunlink(DEFAULT_SMGR, event_relation);	/* Close rel, but keep lock till commit... */	heap_close(event_relation, NoLock);}/* * setRuleCheckAsUser *		Recursively scan a query and set the checkAsUser field to the *		given userid in all rtable entries. * * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD* * RTE entry will be overridden when the view rule is expanded, and the * checkAsUser field of the *NEW* entry is irrelevant because that entry's * checkFor bits will never be set.  However, for other types of rules it's * important to set these fields to match the rule owner.  So we just set * them always. */static voidsetRuleCheckAsUser(Query *qry, AclId userid){	List	   *l;	/* Set all the RTEs in this query node */	foreach(l, qry->rtable)	{		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);		if (rte->rtekind == RTE_SUBQUERY)		{			/* Recurse into subquery in FROM */			setRuleCheckAsUser(rte->subquery, userid);		}		else			rte->checkAsUser = userid;	}	/* If there are sublinks, search for them and process their RTEs */	/* ignore subqueries in rtable because we already processed them */	if (qry->hasSubLinks)		query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid,						  QTW_IGNORE_RT_SUBQUERIES);}/* * Expression-tree walker to find sublink queries */static boolsetRuleCheckAsUser_walker(Node *node, Oid *context){	if (node == NULL)		return false;	if (IsA(node, Query))	{		Query	   *qry = (Query *) node;		setRuleCheckAsUser(qry, *context);		return false;	}	return expression_tree_walker(node, setRuleCheckAsUser_walker,								  (void *) context);}/* * Rename an existing rewrite rule. * * This is unused code at the moment. */voidRenameRewriteRule(Oid owningRel, const char *oldName,				  const char *newName){	Relation	pg_rewrite_desc;	HeapTuple	ruletup;	pg_rewrite_desc = heap_openr(RewriteRelationName, RowExclusiveLock);	ruletup = SearchSysCacheCopy(RULERELNAME,								 ObjectIdGetDatum(owningRel),								 PointerGetDatum(oldName),								 0, 0);	if (!HeapTupleIsValid(ruletup))		ereport(ERROR,				(errcode(ERRCODE_UNDEFINED_OBJECT),				 errmsg("rule \"%s\" for relation \"%s\" does not exist",						oldName, get_rel_name(owningRel))));	/* should not already exist */	if (IsDefinedRewriteRule(owningRel, newName))		ereport(ERROR,				(errcode(ERRCODE_DUPLICATE_OBJECT),				 errmsg("rule \"%s\" for relation \"%s\" already exists",						newName, get_rel_name(owningRel))));	namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName);	simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);	/* keep system catalog indexes current */	CatalogUpdateIndexes(pg_rewrite_desc, ruletup);	heap_freetuple(ruletup);	heap_close(pg_rewrite_desc, RowExclusiveLock);}

⌨️ 快捷键说明

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