rewritehandler.c

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

C
1,349
字号
		((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||		((ArrayRef *) src_tle->expr)->refrestype !=		((ArrayRef *) prior_tle->expr)->refrestype)		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("multiple assignments to same column \"%s\"",						attrName)));	/*	 * Prior TLE could be a nest of ArrayRefs if we do this more than	 * once.	 */	priorbottom = (Node *) ((ArrayRef *) prior_tle->expr)->refexpr;	while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&		   ((ArrayRef *) priorbottom)->refassgnexpr != NULL)		priorbottom = (Node *) ((ArrayRef *) priorbottom)->refexpr;	if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("multiple assignments to same column \"%s\"",						attrName)));	/*	 * Looks OK to nest 'em.	 */	newexpr = makeNode(ArrayRef);	memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));	newexpr->refexpr = prior_tle->expr;	return makeTargetEntry(resdom, (Expr *) newexpr);}/* * Make an expression tree for the default value for a column. * * If there is no default, return a NULL instead. */Node *build_column_default(Relation rel, int attrno){	TupleDesc	rd_att = rel->rd_att;	Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];	Oid			atttype = att_tup->atttypid;	int32		atttypmod = att_tup->atttypmod;	Node	   *expr = NULL;	Oid			exprtype;	/*	 * Scan to see if relation has a default for this column.	 */	if (rd_att->constr && rd_att->constr->num_defval > 0)	{		AttrDefault *defval = rd_att->constr->defval;		int			ndef = rd_att->constr->num_defval;		while (--ndef >= 0)		{			if (attrno == defval[ndef].adnum)			{				/*				 * Found it, convert string representation to node tree.				 */				expr = stringToNode(defval[ndef].adbin);				break;			}		}	}	if (expr == NULL)	{		/*		 * No per-column default, so look for a default for the type		 * itself.		 */		if (att_tup->attisset)		{			/*			 * Set attributes are represented as OIDs no matter what the			 * set element type is, and the element type's default is			 * irrelevant too.			 */		}		else			expr = get_typdefault(atttype);	}	if (expr == NULL)		return NULL;			/* No default anywhere */	/*	 * Make sure the value is coerced to the target column type; this will	 * generally be true already, but there seem to be some corner cases	 * involving domain defaults where it might not be true. This should	 * match the parser's processing of non-defaulted expressions --- see	 * updateTargetListEntry().	 */	exprtype = exprType(expr);	expr = coerce_to_target_type(NULL,	/* no UNKNOWN params here */								 expr, exprtype,								 atttype, atttypmod,								 COERCION_ASSIGNMENT,								 COERCE_IMPLICIT_CAST);	if (expr == NULL)		ereport(ERROR,				(errcode(ERRCODE_DATATYPE_MISMATCH),				 errmsg("column \"%s\" is of type %s"						" but default expression is of type %s",						NameStr(att_tup->attname),						format_type_be(atttype),						format_type_be(exprtype)),		   errhint("You will need to rewrite or cast the expression.")));	return expr;}/* * matchLocks - *	  match the list of locks and returns the matching rules */static List *matchLocks(CmdType event,		   RuleLock *rulelocks,		   int varno,		   Query *parsetree){	List	   *matching_locks = NIL;	int			nlocks;	int			i;	if (rulelocks == NULL)		return NIL;	if (parsetree->commandType != CMD_SELECT)	{		if (parsetree->resultRelation != varno)			return NIL;	}	nlocks = rulelocks->numLocks;	for (i = 0; i < nlocks; i++)	{		RewriteRule *oneLock = rulelocks->rules[i];		if (oneLock->event == event)		{			if (parsetree->commandType != CMD_SELECT ||				(oneLock->attrno == -1 ?				 rangeTableEntry_used((Node *) parsetree, varno, 0) :				 attribute_used((Node *) parsetree,								varno, oneLock->attrno, 0)))				matching_locks = lappend(matching_locks, oneLock);		}	}	return matching_locks;}static Query *ApplyRetrieveRule(Query *parsetree,				  RewriteRule *rule,				  int rt_index,				  bool relation_level,				  Relation relation,				  bool relIsUsed,				  List *activeRIRs){	Query	   *rule_action;	RangeTblEntry *rte,			   *subrte;	if (length(rule->actions) != 1)		elog(ERROR, "expected just one rule action");	if (rule->qual != NULL)		elog(ERROR, "cannot handle qualified ON SELECT rule");	if (!relation_level)		elog(ERROR, "cannot handle per-attribute ON SELECT rule");	/*	 * Make a modifiable copy of the view query, and recursively expand	 * any view references inside it.	 */	rule_action = copyObject(lfirst(rule->actions));	rule_action = fireRIRrules(rule_action, activeRIRs);	/*	 * VIEWs are really easy --- just plug the view query in as a	 * subselect, replacing the relation's original RTE.	 */	rte = rt_fetch(rt_index, parsetree->rtable);	rte->rtekind = RTE_SUBQUERY;	rte->relid = InvalidOid;	rte->subquery = rule_action;	rte->inh = false;			/* must not be set for a subquery */	/*	 * We move the view's permission check data down to its rangetable.	 * The checks will actually be done against the *OLD* entry therein.	 */	subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);	Assert(subrte->relid == relation->rd_id);	subrte->checkForRead = rte->checkForRead;	subrte->checkForWrite = rte->checkForWrite;	subrte->checkAsUser = rte->checkAsUser;	rte->checkForRead = false;	/* no permission check on subquery itself */	rte->checkForWrite = false;	rte->checkAsUser = InvalidOid;	/*	 * FOR UPDATE of view?	 */	if (intMember(rt_index, parsetree->rowMarks))	{		/*		 * Remove the view from the list of rels that will actually be		 * marked FOR UPDATE by the executor.  It will still be access-		 * checked for write access, though.		 */		parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);		/*		 * Set up the view's referenced tables as if FOR UPDATE.		 */		markQueryForUpdate(rule_action, true);	}	return parsetree;}/* * Recursively mark all relations used by a view as FOR UPDATE. * * This may generate an invalid query, eg if some sub-query uses an * aggregate.  We leave it to the planner to detect that. * * NB: this must agree with the parser's transformForUpdate() routine. */static voidmarkQueryForUpdate(Query *qry, bool skipOldNew){	Index		rti = 0;	List	   *l;	foreach(l, qry->rtable)	{		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);		rti++;		/* Ignore OLD and NEW entries if we are at top level of view */		if (skipOldNew &&			(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))			continue;		if (rte->rtekind == RTE_RELATION)		{			if (!intMember(rti, qry->rowMarks))				qry->rowMarks = lappendi(qry->rowMarks, rti);			rte->checkForWrite = true;		}		else if (rte->rtekind == RTE_SUBQUERY)		{			/* FOR UPDATE of subquery is propagated to subquery's rels */			markQueryForUpdate(rte->subquery, false);		}	}}/* * fireRIRonSubLink - *	Apply fireRIRrules() to each SubLink (subselect in expression) found *	in the given tree. * * NOTE: although this has the form of a walker, we cheat and modify the * SubLink nodes in-place.	It is caller's responsibility to ensure that * no unwanted side-effects occur! * * This is unlike most of the other routines that recurse into subselects, * because we must take control at the SubLink node in order to replace * the SubLink's subselect link with the possibly-rewritten subquery. */static boolfireRIRonSubLink(Node *node, List *activeRIRs){	if (node == NULL)		return false;	if (IsA(node, SubLink))	{		SubLink    *sub = (SubLink *) node;		/* Do what we came for */		sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,											   activeRIRs);		/* Fall through to process lefthand args of SubLink */	}	/*	 * Do NOT recurse into Query nodes, because fireRIRrules already	 * processed subselects of subselects for us.	 */	return expression_tree_walker(node, fireRIRonSubLink,								  (void *) activeRIRs);}/* * fireRIRrules - *	Apply all RIR rules on each rangetable entry in a query */static Query *fireRIRrules(Query *parsetree, List *activeRIRs){	int			rt_index;	/*	 * don't try to convert this into a foreach loop, because rtable list	 * can get changed each time through...	 */	rt_index = 0;	while (rt_index < length(parsetree->rtable))	{		RangeTblEntry *rte;		Relation	rel;		List	   *locks;		RuleLock   *rules;		RewriteRule *rule;		LOCKMODE	lockmode;		bool		relIsUsed;		int			i;		++rt_index;		rte = rt_fetch(rt_index, parsetree->rtable);		/*		 * A subquery RTE can't have associated rules, so there's nothing		 * to do to this level of the query, but we must recurse into the		 * subquery to expand any rule references in it.		 */		if (rte->rtekind == RTE_SUBQUERY)		{			rte->subquery = fireRIRrules(rte->subquery, activeRIRs);			continue;		}		/*		 * Joins and other non-relation RTEs can be ignored completely.		 */		if (rte->rtekind != RTE_RELATION)			continue;		/*		 * If the table is not referenced in the query, then we ignore it.		 * This prevents infinite expansion loop due to new rtable entries		 * inserted by expansion of a rule. A table is referenced if it is		 * part of the join set (a source table), or is referenced by any		 * Var nodes, or is the result table.		 */		relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);		if (!relIsUsed && rt_index != parsetree->resultRelation)			continue;		/*		 * This may well be the first access to the relation during the		 * current statement (it will be, if this Query was extracted from		 * a rule or somehow got here other than via the parser).		 * Therefore, grab the appropriate lock type for the relation, and		 * do not release it until end of transaction.	This protects the		 * rewriter and planner against schema changes mid-query.		 *		 * If the relation is the query's result relation, then		 * RewriteQuery() already got the right lock on it, so we need no		 * additional lock. Otherwise, check to see if the relation is		 * accessed FOR UPDATE or not.		 */		if (rt_index == parsetree->resultRelation)			lockmode = NoLock;		else if (intMember(rt_index, parsetree->rowMarks))			lockmode = RowShareLock;		else			lockmode = AccessShareLock;		rel = heap_open(rte->relid, lockmode);		/*		 * Collect the RIR rules that we must apply		 */		rules = rel->rd_rules;		if (rules == NULL)		{			heap_close(rel, NoLock);			continue;		}		locks = NIL;		for (i = 0; i < rules->numLocks; i++)		{			rule = rules->rules[i];			if (rule->event != CMD_SELECT)				continue;			if (rule->attrno > 0)			{				/* per-attr rule; do we need it? */				if (!attribute_used((Node *) parsetree, rt_index,									rule->attrno, 0))					continue;			}			locks = lappend(locks, rule);		}		/*		 * If we found any, apply them --- but first check for recursion!		 */		if (locks != NIL)		{			List	   *newActiveRIRs;			List	   *l;			if (oidMember(RelationGetRelid(rel), activeRIRs))				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("infinite recursion detected in rules for relation \"%s\"",								RelationGetRelationName(rel))));			newActiveRIRs = lconso(RelationGetRelid(rel), activeRIRs);			foreach(l, locks)			{				rule = lfirst(l);				parsetree = ApplyRetrieveRule(parsetree,											  rule,											  rt_index,											  rule->attrno == -1,											  rel,											  relIsUsed,											  newActiveRIRs);			}		}		heap_close(rel, NoLock);

⌨️ 快捷键说明

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