⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rewritehandler.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
{	/* Don't scribble on the passed qual (it's in the relcache!) */	Node	   *new_qual = (Node *) copyObject(rule_qual);	/*	 * In case there are subqueries in the qual, acquire necessary locks and	 * fix any deleted JOIN RTE entries.  (This is somewhat redundant with	 * rewriteRuleAction, but not entirely ... consider restructuring so that	 * we only need to process the qual this way once.)	 */	(void) acquireLocksOnSubLinks(new_qual, NULL);	/* Fix references to OLD */	ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);	/* Fix references to NEW */	if (event == CMD_INSERT || event == CMD_UPDATE)		new_qual = ResolveNew(new_qual,							  PRS2_NEW_VARNO,							  0,							  rt_fetch(rt_index, parsetree->rtable),							  parsetree->targetList,							  event,							  rt_index);	/* And attach the fixed qual */	AddInvertedQual(parsetree, new_qual);	return parsetree;}/* *	fireRules - *	   Iterate through rule locks applying rules. * * Input arguments: *	parsetree - original query *	rt_index - RT index of result relation in original query *	event - type of rule event *	locks - list of rules to fire * Output arguments: *	*instead_flag - set TRUE if any unqualified INSTEAD rule is found *					(must be initialized to FALSE) *	*qual_product - filled with modified original query if any qualified *					INSTEAD rule is found (must be initialized to NULL) * Return value: *	list of rule actions adjusted for use with this query * * Qualified INSTEAD rules generate their action with the qualification * condition added.  They also generate a modified version of the original * query with the negated qualification added, so that it will run only for * rows that the qualified action doesn't act on.  (If there are multiple * qualified INSTEAD rules, we AND all the negated quals onto a single * modified original query.)  We won't execute the original, unmodified * query if we find either qualified or unqualified INSTEAD rules.	If * we find both, the modified original query is discarded too. */static List *fireRules(Query *parsetree,		  int rt_index,		  CmdType event,		  List *locks,		  bool *instead_flag,		  Query **qual_product){	List	   *results = NIL;	ListCell   *l;	foreach(l, locks)	{		RewriteRule *rule_lock = (RewriteRule *) lfirst(l);		Node	   *event_qual = rule_lock->qual;		List	   *actions = rule_lock->actions;		QuerySource qsrc;		ListCell   *r;		/* Determine correct QuerySource value for actions */		if (rule_lock->isInstead)		{			if (event_qual != NULL)				qsrc = QSRC_QUAL_INSTEAD_RULE;			else			{				qsrc = QSRC_INSTEAD_RULE;				*instead_flag = true;	/* report unqualified INSTEAD */			}		}		else			qsrc = QSRC_NON_INSTEAD_RULE;		if (qsrc == QSRC_QUAL_INSTEAD_RULE)		{			/*			 * If there are INSTEAD rules with qualifications, the original			 * query is still performed. But all the negated rule			 * qualifications of the INSTEAD rules are added so it does its			 * actions only in cases where the rule quals of all INSTEAD rules			 * are false. Think of it as the default action in a case. We save			 * this in *qual_product so RewriteQuery() can add it to the query			 * list after we mangled it up enough.			 *			 * If we have already found an unqualified INSTEAD rule, then			 * *qual_product won't be used, so don't bother building it.			 */			if (!*instead_flag)			{				if (*qual_product == NULL)					*qual_product = copyObject(parsetree);				*qual_product = CopyAndAddInvertedQual(*qual_product,													   event_qual,													   rt_index,													   event);			}		}		/* Now process the rule's actions and add them to the result list */		foreach(r, actions)		{			Query	   *rule_action = lfirst(r);			if (rule_action->commandType == CMD_NOTHING)				continue;			rule_action = rewriteRuleAction(parsetree, rule_action,											event_qual, rt_index, event);			rule_action->querySource = qsrc;			rule_action->canSetTag = false;		/* might change later */			results = lappend(results, rule_action);		}	}	return results;}/* * RewriteQuery - *	  rewrites the query and apply the rules again on the queries rewritten * * rewrite_events is a list of open query-rewrite actions, so we can detect * infinite recursion. */static List *RewriteQuery(Query *parsetree, List *rewrite_events){	CmdType		event = parsetree->commandType;	bool		instead = false;	Query	   *qual_product = NULL;	List	   *rewritten = NIL;	/*	 * If the statement is an update, insert or delete - fire rules on it.	 *	 * SELECT rules are handled later when we have all the queries that should	 * get executed.  Also, utilities aren't rewritten at all (do we still	 * need that check?)	 */	if (event != CMD_SELECT && event != CMD_UTILITY)	{		int			result_relation;		RangeTblEntry *rt_entry;		Relation	rt_entry_relation;		List	   *locks;		result_relation = parsetree->resultRelation;		Assert(result_relation != 0);		rt_entry = rt_fetch(result_relation, parsetree->rtable);		Assert(rt_entry->rtekind == RTE_RELATION);		/*		 * We can use NoLock here since either the parser or		 * AcquireRewriteLocks should have locked the rel already.		 */		rt_entry_relation = heap_open(rt_entry->relid, NoLock);		/*		 * If it's an INSERT or UPDATE, rewrite the targetlist into standard		 * form.  This will be needed by the planner anyway, and doing it now		 * ensures that any references to NEW.field will behave sanely.		 */		if (event == CMD_INSERT || event == CMD_UPDATE)			rewriteTargetList(parsetree, rt_entry_relation);		/*		 * Collect and apply the appropriate rules.		 */		locks = matchLocks(event, rt_entry_relation->rd_rules,						   result_relation, parsetree);		if (locks != NIL)		{			List	   *product_queries;			product_queries = fireRules(parsetree,										result_relation,										event,										locks,										&instead,										&qual_product);			/*			 * If we got any product queries, recursively rewrite them --- but			 * first check for recursion!			 */			if (product_queries != NIL)			{				ListCell   *n;				rewrite_event *rev;				foreach(n, rewrite_events)				{					rev = (rewrite_event *) lfirst(n);					if (rev->relation == RelationGetRelid(rt_entry_relation) &&						rev->event == event)						ereport(ERROR,								(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),								 errmsg("infinite recursion detected in rules for relation \"%s\"",							   RelationGetRelationName(rt_entry_relation))));				}				rev = (rewrite_event *) palloc(sizeof(rewrite_event));				rev->relation = RelationGetRelid(rt_entry_relation);				rev->event = event;				rewrite_events = lcons(rev, rewrite_events);				foreach(n, product_queries)				{					Query	   *pt = (Query *) lfirst(n);					List	   *newstuff;					newstuff = RewriteQuery(pt, rewrite_events);					rewritten = list_concat(rewritten, newstuff);				}				rewrite_events = list_delete_first(rewrite_events);			}		}		heap_close(rt_entry_relation, NoLock);	}	/*	 * For INSERTs, the original query is done first; for UPDATE/DELETE, it is	 * done last.  This is needed because update and delete rule actions might	 * not do anything if they are invoked after the update or delete is	 * performed. The command counter increment between the query executions	 * makes the deleted (and maybe the updated) tuples disappear so the scans	 * for them in the rule actions cannot find them.	 *	 * If we found any unqualified INSTEAD, the original query is not done at	 * all, in any form.  Otherwise, we add the modified form if qualified	 * INSTEADs were found, else the unmodified form.	 */	if (!instead)	{		if (parsetree->commandType == CMD_INSERT)		{			if (qual_product != NULL)				rewritten = lcons(qual_product, rewritten);			else				rewritten = lcons(parsetree, rewritten);		}		else		{			if (qual_product != NULL)				rewritten = lappend(rewritten, qual_product);			else				rewritten = lappend(rewritten, parsetree);		}	}	return rewritten;}/* * QueryRewrite - *	  Primary entry point to the query rewriter. *	  Rewrite one query via query rewrite system, possibly returning 0 *	  or many queries. * * NOTE: the parsetree must either have come straight from the parser, * or have been scanned by AcquireRewriteLocks to acquire suitable locks. */List *QueryRewrite(Query *parsetree){	List	   *querylist;	List	   *results = NIL;	ListCell   *l;	CmdType		origCmdType;	bool		foundOriginalQuery;	Query	   *lastInstead;	/*	 * Step 1	 *	 * Apply all non-SELECT rules possibly getting 0 or many queries	 */	querylist = RewriteQuery(parsetree, NIL);	/*	 * Step 2	 *	 * Apply all the RIR rules on each query	 */	foreach(l, querylist)	{		Query	   *query = (Query *) lfirst(l);		query = fireRIRrules(query, NIL);		/*		 * If the query target was rewritten as a view, complain.		 */		if (query->resultRelation)		{			RangeTblEntry *rte = rt_fetch(query->resultRelation,										  query->rtable);			if (rte->rtekind == RTE_SUBQUERY)			{				switch (query->commandType)				{					case CMD_INSERT:						ereport(ERROR,								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),								 errmsg("cannot insert into a view"),								 errhint("You need an unconditional ON INSERT DO INSTEAD rule.")));						break;					case CMD_UPDATE:						ereport(ERROR,								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),								 errmsg("cannot update a view"),								 errhint("You need an unconditional ON UPDATE DO INSTEAD rule.")));						break;					case CMD_DELETE:						ereport(ERROR,								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),								 errmsg("cannot delete from a view"),								 errhint("You need an unconditional ON DELETE DO INSTEAD rule.")));						break;					default:						elog(ERROR, "unrecognized commandType: %d",							 (int) query->commandType);						break;				}			}		}		results = lappend(results, query);	}	/*	 * Step 3	 *	 * Determine which, if any, of the resulting queries is supposed to set	 * the command-result tag; and update the canSetTag fields accordingly.	 *	 * If the original query is still in the list, it sets the command tag.	 * Otherwise, the last INSTEAD query of the same kind as the original is	 * allowed to set the tag.	(Note these rules can leave us with no query	 * setting the tag.  The tcop code has to cope with this by setting up a	 * default tag based on the original un-rewritten query.)	 *	 * The Asserts verify that at most one query in the result list is marked	 * canSetTag.  If we aren't checking asserts, we can fall out of the loop	 * as soon as we find the original query.	 */	origCmdType = parsetree->commandType;	foundOriginalQuery = false;	lastInstead = NULL;	foreach(l, results)	{		Query	   *query = (Query *) lfirst(l);		if (query->querySource == QSRC_ORIGINAL)		{			Assert(query->canSetTag);			Assert(!foundOriginalQuery);			foundOriginalQuery = true;#ifndef USE_ASSERT_CHECKING			break;#endif		}		else		{			Assert(!query->canSetTag);			if (query->commandType == origCmdType &&				(query->querySource == QSRC_INSTEAD_RULE ||				 query->querySource == QSRC_QUAL_INSTEAD_RULE))				lastInstead = query;		}	}	if (!foundOriginalQuery && lastInstead != NULL)		lastInstead->canSetTag = true;	return results;}

⌨️ 快捷键说明

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