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

📄 rewritehandler.c

📁 关系型数据库 Postgresql 6.5.2
💻 C
📖 第 1 页 / 共 5 页
字号:
				else					continue;			}			if (info->action == info->event &&				info->event == CMD_SELECT)				continue;			/*			 * Event Qualification forces copying of parsetree and			 * splitting into two queries one w/rule_qual, one w/NOT			 * rule_qual. Also add user query qual onto rule action			 */			qual = parsetree->qual;			AddQual(info->rule_action, qual);			if (info->rule_qual != NULL)				AddQual(info->rule_action, info->rule_qual);			/*--------------------------------------------------			 * Step 2:			 *	  Rewrite new.attribute w/ right hand side of target-list			 *	  entry for appropriate field name in insert/update			 *--------------------------------------------------			 */			if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))				FixNew(info, parsetree);			/*--------------------------------------------------			 * Step 3:			 *	  rewriting due to retrieve rules			 *--------------------------------------------------			 */			info->rule_action->rtable = info->rt;			/*			 * ProcessRetrieveQuery(info->rule_action, info->rt,			 * &orig_instead_flag, TRUE);			 */			/*--------------------------------------------------			 * Step 4			 *	  Simplify? hey, no algorithm for simplification... let			 *	  the planner do it.			 *--------------------------------------------------			 */			results = lappend(results, info->rule_action);			pfree(info);		}		/* ----------		 * If this was an unqualified instead rule,		 * throw away an eventually saved 'default' parsetree		 * ----------		 */		if (event_qual == NULL && *instead_flag)			*qual_products = NIL;	}	return results;}static List *RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products){	CmdType		event;	List	   *product_queries = NIL;	int			result_relation = 0;	RangeTblEntry *rt_entry;	Relation	rt_entry_relation = NULL;	RuleLock   *rt_entry_locks = NULL;	Assert(parsetree != NULL);	event = parsetree->commandType;	/*	 * SELECT rules are handled later when we have all the queries that	 * should get executed	 */	if (event == CMD_SELECT)		return NIL;	/*	 * Utilities aren't rewritten at all - why is this here?	 */	if (event == CMD_UTILITY)		return NIL;	/*	 * only for a delete may the targetlist be NULL	 */	if (event != CMD_DELETE)		Assert(parsetree->targetList != NULL);	result_relation = parsetree->resultRelation;	/*	 * the statement is an update, insert or delete - fire rules on it.	 */	rt_entry = rt_fetch(result_relation, parsetree->rtable);	rt_entry_relation = heap_openr(rt_entry->relname);	rt_entry_locks = rt_entry_relation->rd_rules;	heap_close(rt_entry_relation);	if (rt_entry_locks != NULL)	{		List	   *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree);		product_queries = fireRules(parsetree,									result_relation,									event,									instead_flag,									locks,									qual_products);	}	return product_queries;}/* * to avoid infinite recursion, we restrict the number of times a query * can be rewritten. Detecting cycles is left for the reader as an excercise. */#ifndef REWRITE_INVOKE_MAX#define REWRITE_INVOKE_MAX		10#endifstatic int	numQueryRewriteInvoked = 0;/* * deepRewriteQuery - *	  rewrites the query and apply the rules again on the queries rewritten */static List *deepRewriteQuery(Query *parsetree){	List	   *n;	List	   *rewritten = NIL;	List	   *result = NIL;	bool		instead;	List	   *qual_products = NIL;	if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)	{		elog(ERROR, "query rewritten %d times, may contain cycles",			 numQueryRewriteInvoked - 1);	}	instead = FALSE;	result = RewriteQuery(parsetree, &instead, &qual_products);	foreach(n, result)	{		Query	   *pt = lfirst(n);		List	   *newstuff = NIL;		newstuff = deepRewriteQuery(pt);		if (newstuff != NIL)			rewritten = nconc(rewritten, newstuff);	}	/* ----------	 * qual_products are the original query with the negated	 * rule qualification of an instead rule	 * ----------	 */	if (qual_products != NIL)		rewritten = nconc(rewritten, qual_products);	/* ----------	 * The original query is appended last if not instead	 * 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 execution makes the deleted (and	 * maybe the updated) tuples disappear so the scans	 * for them in the rule actions cannot find them.	 * ----------	 */	if (!instead)		rewritten = lappend(rewritten, parsetree);	return rewritten;}/* * QueryOneRewrite - *	  rewrite one query */static List *QueryRewriteOne(Query *parsetree){	numQueryRewriteInvoked = 0;	/*	 * take a deep breath and apply all the rewrite rules - ay	 */	return deepRewriteQuery(parsetree);}/* ---------- * RewritePreprocessQuery - *	adjust details in the parsetree, the rule system *	depends on * ---------- */static voidRewritePreprocessQuery(Query *parsetree){	/* ----------	 * if the query has a resultRelation, reassign the	 * result domain numbers to the attribute numbers in the	 * target relation. FixNew() depends on it when replacing	 * *new* references in a rule action by the expressions	 * from the rewritten query.	 * resjunk targets are somewhat arbitrarily given a resno of 0;	 * this is to prevent FixNew() from matching them to var nodes.	 * ----------	 */	if (parsetree->resultRelation > 0)	{		RangeTblEntry *rte;		Relation	rd;		List	   *tl;		rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,									parsetree->rtable);		rd = heap_openr(rte->relname);		foreach(tl, parsetree->targetList)		{			TargetEntry *tle = (TargetEntry *) lfirst(tl);			if (! tle->resdom->resjunk)				tle->resdom->resno = attnameAttNum(rd, tle->resdom->resname);			else				tle->resdom->resno = 0;		}		heap_close(rd);	}}/* * BasicQueryRewrite - *	  rewrite one query via query rewrite system, possibly returning 0 *	  or many queries */static List *BasicQueryRewrite(Query *parsetree){	List	   *querylist;	List	   *results = NIL;	List	   *l;	Query	   *query;	/*	 * Step 1	 *	 * There still seems something broken with the resdom numbers so we	 * reassign them first.	 */	RewritePreprocessQuery(parsetree);	/*	 * Step 2	 *	 * Apply all non-SELECT rules possibly getting 0 or many queries	 */	querylist = QueryRewriteOne(parsetree);	/*	 * Step 3	 *	 * Apply all the RIR rules on each query	 */	foreach(l, querylist)	{		query = fireRIRrules((Query *) lfirst(l));		/*		 * If the query was marked having aggregates, check if this is		 * still true after rewriting. This check must get expanded when		 * someday aggregates can appear somewhere else than in the		 * targetlist or the having qual.		 */		if (query->hasAggs)			query->hasAggs = checkQueryHasAggs((Node *) (query->targetList))				| checkQueryHasAggs((Node *) (query->havingQual));		query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual))			| checkQueryHasSubLink((Node *) (query->havingQual));		results = lappend(results, query);	}	return results;}/* * QueryRewrite - *	  Primary entry point to the query rewriter. *	  Rewrite one query via query rewrite system, possibly returning 0 *	  or many queries. * * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was * moved here so that it would be invoked during EXPLAIN.  The division of * labor between this routine and BasicQueryRewrite is not obviously correct * ... at least not to me ... tgl 5/99. */List *QueryRewrite(Query *parsetree){	List	   *rewritten,			   *rewritten_item;	/***S*I***/	/*	 * Rewrite Union, Intersect and Except Queries to normal Union Queries	 * using IN and NOT IN subselects	 */	if (parsetree->intersectClause)		parsetree = Except_Intersect_Rewrite(parsetree);	/* Rewrite basic queries (retrieve, append, delete, replace) */	rewritten = BasicQueryRewrite(parsetree);	/*	 * Rewrite the UNIONS.	 */	foreach(rewritten_item, rewritten)	{		Query	   *qry = (Query *) lfirst(rewritten_item);		List	   *union_result = NIL;		List	   *union_item;		foreach(union_item, qry->unionClause)		{			union_result = nconc(union_result,						BasicQueryRewrite((Query *) lfirst(union_item)));		}		qry->unionClause = union_result;	}	return rewritten;}/***S*I***//* This function takes two targetlists as arguments and checks if the * targetlists are compatible (i.e. both select for the same number of * attributes and the types are compatible */static voidcheck_targetlists_are_compatible(List *prev_target, List *current_target){	List	   *tl,			   *next_target;	int			prev_len = 0,				next_len = 0;	foreach(tl, prev_target)		if (!((TargetEntry *) lfirst(tl))->resdom->resjunk)		prev_len++;	foreach(next_target, current_target)		if (!((TargetEntry *) lfirst(next_target))->resdom->resjunk)		next_len++;	if (prev_len != next_len)		elog(ERROR, "Each UNION | EXCEPT | INTERSECT query must have the same number of columns.");	foreach(next_target, current_target)	{		Oid			itype;		Oid			otype;		otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;		itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;		/* one or both is a NULL column? then don't convert... */		if (otype == InvalidOid)		{			/* propagate a known type forward, if available */			if (itype != InvalidOid)				((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;#ifdef NOT_USED			else			{				((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;				((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;			}#endif		}		else if (itype == InvalidOid)		{		}		/* they don't match in type? then convert... */		else if (itype != otype)		{			Node	   *expr;			expr = ((TargetEntry *) lfirst(next_target))->expr;			expr = CoerceTargetExpr(NULL, expr, itype, otype);			if (expr == NULL)			{				elog(ERROR, "Unable to transform %s to %s"					 "\n\tEach UNION | EXCEPT | INTERSECT clause must have compatible target types",					 typeidTypeName(itype),					 typeidTypeName(otype));			}			((TargetEntry *) lfirst(next_target))->expr = expr;			((TargetEntry *) lfirst(next_target))->resdom->restype = otype;		}		/* both are UNKNOWN? then evaluate as text... */		else if (itype == UNKNOWNOID)		{			((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;			((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;		}		prev_target = lnext(prev_target);	}}/***S*I***//* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent * queries that use IN and NOT IN subselects. * * The operator tree is attached to 'intersectClause' (see rule * 'SelectStmt' in gram.y) of the 'parsetree' given as an * argument. First we remember some clauses (the sortClause, the * unique flag etc.)  Then we translate the operator tree to DNF * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr() * earlier we get DNF after exchanging ANDs and ORs again in the * result.) Now we create a new query by evaluating the new operator * tree which is in DNF now. For every AND we create an entry in the * union list and for every OR we create an IN subselect. (NOT IN * subselects are created for OR NOT nodes). The first entry of the * union list is handed back but before that the remembered clauses * (sortClause etc) are attached to the new top Node (Note that the * new top Node can differ from the parsetree given as argument because of * the translation to DNF. That's why we have to remember the sortClause or * unique flag!) */static Query *Except_Intersect_Rewrite(Query *parsetree){	SubLink    *n;	Query	   *result,			   *intersect_node;	List	   *elist,			   *intersect_list = NIL,			   *intersect,			   *intersectClause;	List	   *union_list = NIL,			   *sortClause;	List	   *left_expr,			   *right_expr,			   *resnames = NIL;	char	   *op,			   *uniqueFlag,			   *into;	bool		isBinary,				isPortal,				isTemp;	CmdType		commandType = CMD_SELECT;	List	   *rtable_insert = NIL;	List	   *prev_target = NIL;	/*	 * Remember the Resnames of the given parsetree's targetlist (these	 * are the resnames of the first Select Statement of the query	 * formulated by the user and he wants the columns named by these	 * strings. The transformation to DNF can cause another Select	 * Statment to be the top one which uses other names for its columns.	 * Therefore we remeber the original names and attach them to the	 * targetlist of the new topmost Node at the end of this function	 */	foreach(elist, parsetree->targetList)	{		TargetEntry *tent = (TargetEntry *) lfirst(elist);		resnames = lappend(resnames, tent->resdom->resname);	}	/*	 * If the Statement is an INSERT INTO ... (SELECT...) statement using	 * UNIONs, INTERSECTs or EXCEPTs and the transformation to DNF makes	 * another Node to the top node we have to transform the new top node	 * to an INSERT node and the original INSERT node to a SELECT node	 */	if (parsetree->commandType == CMD_INSERT)	{		parsetree->commandType = CMD_SELECT;		commandType = CMD_INSERT;		parsetree->resultRelation = 0;		/*		 * The result relation ( = the one to insert into) has to be		 * attached to the rtable list of the new top node		 */		rtable_insert = nth(length(parsetree->rtable) - 1, parsetree->rtable);	}	/*	 * Save some items, to be able to attach them to the resulting top	 * node at the end of the function	 */	sortClause = parsetree->sortClause;	uniqueFlag = parsetree->uniqueFlag;	into = parsetree->into;	isBinary = parsetree->isBinary;	isPortal = parsetree->isPortal;	isTemp = parsetree->isTemp;	/*	 * The operator tree attached to parsetree->intersectClause is still	 * 'raw' ( = the leaf nodes are still SelectStmt nodes instead of	 * Query nodes) So step through the tree and transform the nodes using	 * parse_analyze().	 *	 * The parsetree (given as an argument to Except_Intersect_Rewrite()) has	 * already been transformed and transforming it again would cause	 * troubles.  So we give the 'raw' version (of the cooked parsetree)	 * to the function to prevent an additional transformation. Instead we	 * hand back the 'cooked' version also given as an argument to	 * intersect_tree_analyze()	 */	intersectClause =		(List *) intersect_tree_analyze((Node *) parsetree->intersectClause,								 (Node *) lfirst(parsetree->unionClause),										(Node *) parsetree);	/* intersectClause is no longer needed so set it to NIL */	parsetree->intersectClause = NIL;	/*	 * unionClause will be needed later on but the list it delivered is no	 * longer needed, so set it to NIL	 */	parsetree->unionClause = NIL;	/*	 * Transform the operator tree to DNF (remember ANDs and ORs have been	 * exchanged, that's why we get DNF by using cnfify)	 *	 * After the call, explicit ANDs are removed and all AND operands are	 * simply items in the intersectClause list	 */	intersectClause = cnfify((Expr *) intersectClause, true);	/*	 * For every entry of the intersectClause list we generate one entry	 * in the union_list	 */	foreach(intersect, intersectClause)	{		/*		 * for every OR we create an IN subselect and for every OR NOT we		 * create a NOT IN subselect, so first extract all the Select		 * Query nodes from the tree (that contains only OR or OR NOTs any		 * more because we did a transformation to DNF		 *		 * There must be at least one node that is not negated (i.e. just OR		 * and not OR NOT) and this node will be the first in the list		 * returned		 */		intersect_list = NIL;		create_intersect_list((Node *) lfirst(intersect), &intersect_list);		/*		 * This one will become the Select Query node, all other nodes are		 * transformed into subselects under this node!		 */		intersect_node = (Query *) lfirst(intersect_list);		intersect_list = lnext(intersect_list);		/*		 * Check if all Select Statements use the same number of		 * attributes and if all corresponding attributes are of the same		 * type		 */		if (prev_targe

⌨️ 快捷键说明

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