rewritehandler.c

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

C
1,349
字号
/*------------------------------------------------------------------------- * * rewriteHandler.c *		Primary module of query rewriter. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.130.2.1 2004/01/14 03:39:29 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 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 void markQueryForUpdate(Query *qry, bool skipOldNew);static List *matchLocks(CmdType event, RuleLock *rulelocks,		   int varno, Query *parsetree);static Query *fireRIRrules(Query *parsetree, List *activeRIRs);/* * 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);	current_varno = rt_index;	rt_length = 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 = nconc((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 =				nconc(newjointree, sub_action->jointree->fromlist);		}	}	/*	 * We copy the qualifications of the parsetree to the action and vice	 * versa. So force hasSubLinks if one of them has it. If this is not	 * right, the flag will get cleared later, but we mustn't risk having	 * it not set when it needs to be.	(XXX this should probably be	 * handled by AddQual and friends, not here...)	 */	if (parsetree->hasSubLinks)		sub_action->hasSubLinks = TRUE;	else if (sub_action->hasSubLinks)		parsetree->hasSubLinks = TRUE;	/*	 * 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)	{		sub_action = (Query *) ResolveNew((Node *) sub_action,										  new_varno,										  0,										  parsetree->targetList,										  event,										  current_varno);		if (sub_action_ptr)			*sub_action_ptr = sub_action;		else			rule_action = sub_action;	}	return rule_action;}/* * Copy the query's jointree list, and optionally attempt to remove any * occurrence of the given rt_index as a top-level join item (we do not look * for it within join items; this is OK because we are only expecting to find * it as an UPDATE or DELETE target relation, which will be at the top level * of the join).  Returns modified jointree list --- this is a separate copy * sharing no nodes with the original. */static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index){	List	   *newjointree = copyObject(parsetree->jointree->fromlist);	List	   *jjt;	if (removert)	{		foreach(jjt, newjointree)		{			RangeTblRef *rtr = lfirst(jjt);			if (IsA(rtr, RangeTblRef) &&				rtr->rtindex == rt_index)			{				newjointree = lremove(rtr, newjointree);				/* foreach is safe because we exit loop after lremove... */				break;			}		}	}	return newjointree;}/* * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form * * This has the following responsibilities: * * 1. For an INSERT, add tlist entries to compute default values for any * attributes that have defaults and are not assigned to in the given tlist. * (We do not insert anything for default-less attributes, however.  The * planner will later insert NULLs for them, but there's no reason to slow * down rewriter processing with extra tlist nodes.)  Also, for both INSERT * and UPDATE, replace explicit DEFAULT specifications with column default * expressions. * * 2. Merge multiple entries for the same target attribute, or declare error * if we can't.  Presently, multiple entries are only allowed for UPDATE of * an array field, for example "UPDATE table SET foo[2] = 42, foo[4] = 43". * We can merge such operations into a single assignment op.  Essentially, * the expression we want to produce in this case is like *		foo = array_set(array_set(foo, 2, 42), 4, 43) * * 3. Sort the tlist into standard order: non-junk fields in order by resno, * then junk fields (these in no particular order). * * We must do items 1 and 2 before firing rewrite rules, else rewritten * references to NEW.foo will produce wrong or incomplete results.	Item 3 * is not needed for rewriting, but will be needed by the planner, and we * can do it essentially for free while handling items 1 and 2. */static voidrewriteTargetList(Query *parsetree, Relation target_relation){	CmdType		commandType = parsetree->commandType;	List	   *tlist = parsetree->targetList;	List	   *new_tlist = NIL;	int			attrno,				numattrs;	List	   *temp;	/*	 * Scan the tuple description in the relation's relcache entry to make	 * sure we have all the user attributes in the right order.	 */	numattrs = RelationGetNumberOfAttributes(target_relation);	for (attrno = 1; attrno <= numattrs; attrno++)	{		Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno - 1];		TargetEntry *new_tle = NULL;		/* We can ignore deleted attributes */		if (att_tup->attisdropped)			continue;		/*		 * Look for targetlist entries matching this attr.		 *		 * Junk attributes are not candidates to be matched.		 */		foreach(temp, tlist)		{			TargetEntry *old_tle = (TargetEntry *) lfirst(temp);			Resdom	   *resdom = old_tle->resdom;			if (!resdom->resjunk && resdom->resno == attrno)			{				new_tle = process_matched_tle(old_tle, new_tle,											  NameStr(att_tup->attname));				/* keep scanning to detect multiple assignments to attr */			}		}		/*		 * Handle the two cases where we need to insert a default		 * expression: it's an INSERT and there's no tlist entry for the		 * column, or the tlist entry is a DEFAULT placeholder node.		 */		if ((new_tle == NULL && commandType == CMD_INSERT) ||		  (new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))		{			Node	   *new_expr;			new_expr = build_column_default(target_relation, attrno);			/*			 * If there is no default (ie, default is effectively NULL),			 * we can omit the tlist entry in the INSERT case, since the			 * planner can insert a NULL for itself, and there's no point			 * in spending any more rewriter cycles on the entry.  But in			 * the UPDATE case we've got to explicitly set the column to			 * NULL.			 */			if (!new_expr)			{				if (commandType == CMD_INSERT)					new_tle = NULL;				else				{					new_expr = (Node *) makeConst(att_tup->atttypid,												  att_tup->attlen,												  (Datum) 0,												  true, /* isnull */												  att_tup->attbyval);					/* this is to catch a NOT NULL domain constraint */					new_expr = coerce_to_domain(new_expr,												InvalidOid,												att_tup->atttypid,												COERCE_IMPLICIT_CAST);				}			}			if (new_expr)				new_tle = makeTargetEntry(makeResdom(attrno,													 att_tup->atttypid,													 att_tup->atttypmod,									  pstrdup(NameStr(att_tup->attname)),													 false),										  (Expr *) new_expr);		}		if (new_tle)			new_tlist = lappend(new_tlist, new_tle);	}	/*	 * Copy all resjunk tlist entries to the end of the new tlist, and	 * assign them resnos above the last real resno.	 *	 * Typical junk entries include ORDER BY or GROUP BY expressions (are	 * these actually possible in an INSERT or UPDATE?), system attribute	 * references, etc.	 */	foreach(temp, tlist)	{		TargetEntry *old_tle = (TargetEntry *) lfirst(temp);		Resdom	   *resdom = old_tle->resdom;		if (resdom->resjunk)		{			/* Get the resno right, but don't copy unnecessarily */			if (resdom->resno != attrno)			{				resdom = (Resdom *) copyObject((Node *) resdom);				resdom->resno = attrno;				old_tle = makeTargetEntry(resdom, old_tle->expr);			}			new_tlist = lappend(new_tlist, old_tle);			attrno++;		}		else		{			/* Let's just make sure we processed all the non-junk items */			if (resdom->resno < 1 || resdom->resno > numattrs)				elog(ERROR, "bogus resno %d in targetlist", resdom->resno);		}	}	parsetree->targetList = new_tlist;}/* * Convert a matched TLE from the original tlist into a correct new TLE. * * This routine detects and handles multiple assignments to the same target * attribute.  (The attribute name is needed only for error messages.) */static TargetEntry *process_matched_tle(TargetEntry *src_tle,					TargetEntry *prior_tle,					const char *attrName){	Resdom	   *resdom = src_tle->resdom;	Node	   *priorbottom;	ArrayRef   *newexpr;	if (prior_tle == NULL)	{		/*		 * Normal case where this is the first assignment to the		 * attribute.		 */		return src_tle;	}	/*	 * Multiple assignments to same attribute.	Allow only if all are	 * array-assign operators with same bottom array object.	 */	if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||		((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||		prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||

⌨️ 快捷键说明

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