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

📄 rewritehandler.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
/*------------------------------------------------------------------------- * * rewriteHandler.c *		Primary module of query rewriter. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.158.2.2 2005/11/23 17:21:22 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "optimizer/var.h"#include "parser/analyze.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_type.h"#include "parser/parsetree.h"#include "rewrite/rewriteHandler.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/lsyscache.h"/* We use a list of these to detect recursion in RewriteQuery */typedef struct rewrite_event{	Oid			relation;		/* OID of relation having rules */	CmdType		event;			/* type of rule being fired */} rewrite_event;static bool acquireLocksOnSubLinks(Node *node, void *context);static Query *rewriteRuleAction(Query *parsetree,				  Query *rule_action,				  Node *rule_qual,				  int rt_index,				  CmdType event);static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);static void rewriteTargetList(Query *parsetree, Relation target_relation);static TargetEntry *process_matched_tle(TargetEntry *src_tle,					TargetEntry *prior_tle,					const char *attrName);static Node *get_assignment_input(Node *node);static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait,					bool skipOldNew);static List *matchLocks(CmdType event, RuleLock *rulelocks,		   int varno, Query *parsetree);static Query *fireRIRrules(Query *parsetree, List *activeRIRs);/* * AcquireRewriteLocks - *	  Acquire suitable locks on all the relations mentioned in the Query. *	  These locks will ensure that the relation schemas don't change under us *	  while we are rewriting and planning the query. * * A secondary purpose of this routine is to fix up JOIN RTE references to * dropped columns (see details below).  Because the RTEs are modified in * place, it is generally appropriate for the caller of this routine to have * first done a copyObject() to make a writable copy of the querytree in the * current memory context. * * This processing can, and for efficiency's sake should, be skipped when the * querytree has just been built by the parser: parse analysis already got * all the same locks we'd get here, and the parser will have omitted dropped * columns from JOINs to begin with.  But we must do this whenever we are * dealing with a querytree produced earlier than the current command. * * About JOINs and dropped columns: although the parser never includes an * already-dropped column in a JOIN RTE's alias var list, it is possible for * such a list in a stored rule to include references to dropped columns. * (If the column is not explicitly referenced anywhere else in the query, * the dependency mechanism won't consider it used by the rule and so won't * prevent the column drop.)  To support get_rte_attribute_is_dropped(), * we replace join alias vars that reference dropped columns with NULL Const * nodes. * * (In PostgreSQL 8.0, we did not do this processing but instead had * get_rte_attribute_is_dropped() recurse to detect dropped columns in joins. * That approach had horrible performance unfortunately; in particular * construction of a nested join was O(N^2) in the nesting depth.) */voidAcquireRewriteLocks(Query *parsetree){	ListCell   *l;	int			rt_index;	/*	 * First, process RTEs of the current query level.	 */	rt_index = 0;	foreach(l, parsetree->rtable)	{		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);		Relation	rel;		LOCKMODE	lockmode;		List	   *newaliasvars;		Index		curinputvarno;		RangeTblEntry *curinputrte;		ListCell   *ll;		++rt_index;		switch (rte->rtekind)		{			case RTE_RELATION:				/*				 * 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 we				 * need RowExclusiveLock.  Otherwise, check to see if the				 * relation is accessed FOR UPDATE/SHARE or not.  We can't				 * just grab AccessShareLock because then the executor would				 * be trying to upgrade the lock, leading to possible				 * deadlocks.				 */				if (rt_index == parsetree->resultRelation)					lockmode = RowExclusiveLock;				else if (list_member_int(parsetree->rowMarks, rt_index))					lockmode = RowShareLock;				else					lockmode = AccessShareLock;				rel = heap_open(rte->relid, lockmode);				heap_close(rel, NoLock);				break;			case RTE_JOIN:				/*				 * Scan the join's alias var list to see if any columns have				 * been dropped, and if so replace those Vars with NULL				 * Consts.				 *				 * Since a join has only two inputs, we can expect to see				 * multiple references to the same input RTE; optimize away				 * multiple fetches.				 */				newaliasvars = NIL;				curinputvarno = 0;				curinputrte = NULL;				foreach(ll, rte->joinaliasvars)				{					Var		   *aliasvar = (Var *) lfirst(ll);					/*					 * If the list item isn't a simple Var, then it must					 * represent a merged column, ie a USING column, and so it					 * couldn't possibly be dropped, since it's referenced in					 * the join clause.  (Conceivably it could also be a NULL					 * constant already?  But that's OK too.)					 */					if (IsA(aliasvar, Var))					{						/*						 * The elements of an alias list have to refer to						 * earlier RTEs of the same rtable, because that's the						 * order the planner builds things in.	So we already						 * processed the referenced RTE, and so it's safe to						 * use get_rte_attribute_is_dropped on it. (This might						 * not hold after rewriting or planning, but it's OK						 * to assume here.)						 */						Assert(aliasvar->varlevelsup == 0);						if (aliasvar->varno != curinputvarno)						{							curinputvarno = aliasvar->varno;							if (curinputvarno >= rt_index)								elog(ERROR, "unexpected varno %d in JOIN RTE %d",									 curinputvarno, rt_index);							curinputrte = rt_fetch(curinputvarno,												   parsetree->rtable);						}						if (get_rte_attribute_is_dropped(curinputrte,														 aliasvar->varattno))						{							/*							 * can't use vartype here, since that might be a							 * now-dropped type OID, but it doesn't really							 * matter what type the Const claims to be.							 */							aliasvar = (Var *) makeNullConst(INT4OID);						}					}					newaliasvars = lappend(newaliasvars, aliasvar);				}				rte->joinaliasvars = newaliasvars;				break;			case RTE_SUBQUERY:				/*				 * The subquery RTE itself is all right, but we have to				 * recurse to process the represented subquery.				 */				AcquireRewriteLocks(rte->subquery);				break;			default:				/* ignore other types of RTEs */				break;		}	}	/*	 * Recurse into sublink subqueries, too.  But we already did the ones in	 * the rtable.	 */	if (parsetree->hasSubLinks)		query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,						  QTW_IGNORE_RT_SUBQUERIES);}/* * Walker to find sublink subqueries for AcquireRewriteLocks */static boolacquireLocksOnSubLinks(Node *node, void *context){	if (node == NULL)		return false;	if (IsA(node, SubLink))	{		SubLink    *sub = (SubLink *) node;		/* Do what we came for */		AcquireRewriteLocks((Query *) sub->subselect);		/* Fall through to process lefthand args of SubLink */	}	/*	 * Do NOT recurse into Query nodes, because AcquireRewriteLocks already	 * processed subselects of subselects for us.	 */	return expression_tree_walker(node, acquireLocksOnSubLinks, context);}/* * rewriteRuleAction - *	  Rewrite the rule action with appropriate qualifiers (taken from *	  the triggering query). */static Query *rewriteRuleAction(Query *parsetree,				  Query *rule_action,				  Node *rule_qual,				  int rt_index,				  CmdType event){	int			current_varno,				new_varno;	int			rt_length;	Query	   *sub_action;	Query	  **sub_action_ptr;	/*	 * Make modifiable copies of rule action and qual (what we're passed are	 * the stored versions in the relcache; don't touch 'em!).	 */	rule_action = (Query *) copyObject(rule_action);	rule_qual = (Node *) copyObject(rule_qual);	/*	 * Acquire necessary locks and fix any deleted JOIN RTE entries.	 */	AcquireRewriteLocks(rule_action);	(void) acquireLocksOnSubLinks(rule_qual, NULL);	current_varno = rt_index;	rt_length = list_length(parsetree->rtable);	new_varno = PRS2_NEW_VARNO + rt_length;	/*	 * Adjust rule action and qual to offset its varnos, so that we can merge	 * its rtable with the main parsetree's rtable.	 *	 * If the rule action is an INSERT...SELECT, the OLD/NEW rtable entries	 * will be in the SELECT part, and we have to modify that rather than the	 * top-level INSERT (kluge!).	 */	sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr);	OffsetVarNodes((Node *) sub_action, rt_length, 0);	OffsetVarNodes(rule_qual, rt_length, 0);	/* but references to *OLD* should point at original rt_index */	ChangeVarNodes((Node *) sub_action,				   PRS2_OLD_VARNO + rt_length, rt_index, 0);	ChangeVarNodes(rule_qual,				   PRS2_OLD_VARNO + rt_length, rt_index, 0);	/*	 * Generate expanded rtable consisting of main parsetree's rtable plus	 * rule action's rtable; this becomes the complete rtable for the rule	 * action.	Some of the entries may be unused after we finish rewriting,	 * but we leave them all in place for two reasons:	 *	 * We'd have a much harder job to adjust the query's varnos if we	 * selectively removed RT entries.	 *	 * If the rule is INSTEAD, then the original query won't be executed at	 * all, and so its rtable must be preserved so that the executor will do	 * the correct permissions checks on it.	 *	 * RT entries that are not referenced in the completed jointree will be	 * ignored by the planner, so they do not affect query semantics.  But any	 * permissions checks specified in them will be applied during executor	 * startup (see ExecCheckRTEPerms()).  This allows us to check that the	 * caller has, say, insert-permission on a view, when the view is not	 * semantically referenced at all in the resulting query.	 *	 * When a rule is not INSTEAD, the permissions checks done on its copied	 * RT entries will be redundant with those done during execution of the	 * original query, but we don't bother to treat that case differently.	 *	 * NOTE: because planner will destructively alter rtable, we must ensure	 * that rule action's rtable is separate and shares no substructure with	 * the main rtable.  Hence do a deep copy here.	 */	sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),									 sub_action->rtable);	/*	 * Each rule action's jointree should be the main parsetree's jointree	 * plus that rule's jointree, but usually *without* the original rtindex	 * that we're replacing (if present, which it won't be for INSERT). Note	 * that if the rule action refers to OLD, its jointree will add a	 * reference to rt_index.  If the rule action doesn't refer to OLD, but	 * either the rule_qual or the user query quals do, then we need to keep	 * the original rtindex in the jointree to provide data for the quals.	We	 * don't want the original rtindex to be joined twice, however, so avoid	 * keeping it if the rule action mentions it.	 *	 * As above, the action's jointree must not share substructure with the	 * main parsetree's.	 */	if (sub_action->commandType != CMD_UTILITY)	{		bool		keeporig;		List	   *newjointree;		Assert(sub_action->jointree != NULL);		keeporig = (!rangeTableEntry_used((Node *) sub_action->jointree,										  rt_index, 0)) &&			(rangeTableEntry_used(rule_qual, rt_index, 0) ||			 rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0));		newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index);		if (newjointree != NIL)		{			/*			 * If sub_action is a setop, manipulating its jointree will do no			 * good at all, because the jointree is dummy.	(Perhaps someday			 * we could push the joining and quals down to the member			 * statements of the setop?)			 */			if (sub_action->setOperations != NULL)				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));			sub_action->jointree->fromlist =				list_concat(newjointree, sub_action->jointree->fromlist);			/*			 * There could have been some SubLinks in newjointree, in which			 * case we'd better mark the sub_action correctly.			 */			if (parsetree->hasSubLinks && !sub_action->hasSubLinks)				sub_action->hasSubLinks =					checkExprHasSubLink((Node *) newjointree);		}	}	/*	 * 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	 */	AddQual(sub_action, rule_qual);	AddQual(sub_action, parsetree->jointree->quals);	/*	 * Rewrite new.attribute w/ right hand side of target-list entry for	 * appropriate field name in insert/update.	 *	 * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just	 * apply it to sub_action; we have to remember to update the sublink	 * inside rule_action, too.	 */	if ((event == CMD_INSERT || event == CMD_UPDATE) &&

⌨️ 快捷键说明

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