rewritemanip.c

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

C
971
字号
 *	in var nodes or join or setOp trees of a query or expression. */typedef struct{	int			rt_index;	int			sublevels_up;} rangeTableEntry_used_context;static boolrangeTableEntry_used_walker(Node *node,							rangeTableEntry_used_context *context){	if (node == NULL)		return false;	if (IsA(node, Var))	{		Var		   *var = (Var *) node;		if (var->varlevelsup == context->sublevels_up &&			var->varno == context->rt_index)			return true;		return false;	}	if (IsA(node, RangeTblRef))	{		RangeTblRef *rtr = (RangeTblRef *) node;		if (rtr->rtindex == context->rt_index &&			context->sublevels_up == 0)			return true;		/* the subquery itself is visited separately */		return false;	}	if (IsA(node, JoinExpr))	{		JoinExpr   *j = (JoinExpr *) node;		if (j->rtindex == context->rt_index &&			context->sublevels_up == 0)			return true;		/* fall through to examine children */	}	if (IsA(node, InClauseInfo))	{		InClauseInfo *ininfo = (InClauseInfo *) node;		if (context->sublevels_up == 0 &&			(bms_is_member(context->rt_index, ininfo->lefthand) ||			 bms_is_member(context->rt_index, ininfo->righthand)))			return true;		/* fall through to examine children */	}	if (IsA(node, Query))	{		/* Recurse into subselects */		bool		result;		context->sublevels_up++;		result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,								   (void *) context, 0);		context->sublevels_up--;		return result;	}	return expression_tree_walker(node, rangeTableEntry_used_walker,								  (void *) context);}boolrangeTableEntry_used(Node *node, int rt_index, int sublevels_up){	rangeTableEntry_used_context context;	context.rt_index = rt_index;	context.sublevels_up = sublevels_up;	/*	 * Must be prepared to start with a Query or a bare expression tree;	 * if it's a Query, we don't want to increment sublevels_up.	 */	return query_or_expression_tree_walker(node,										   rangeTableEntry_used_walker,										   (void *) &context,										   0);}/* * attribute_used - *	Check if a specific attribute number of a RTE is used *	somewhere in the query or expression. */typedef struct{	int			rt_index;	int			attno;	int			sublevels_up;} attribute_used_context;static boolattribute_used_walker(Node *node,					  attribute_used_context *context){	if (node == NULL)		return false;	if (IsA(node, Var))	{		Var		   *var = (Var *) node;		if (var->varlevelsup == context->sublevels_up &&			var->varno == context->rt_index &&			var->varattno == context->attno)			return true;		return false;	}	if (IsA(node, Query))	{		/* Recurse into subselects */		bool		result;		context->sublevels_up++;		result = query_tree_walker((Query *) node, attribute_used_walker,								   (void *) context, 0);		context->sublevels_up--;		return result;	}	return expression_tree_walker(node, attribute_used_walker,								  (void *) context);}boolattribute_used(Node *node, int rt_index, int attno, int sublevels_up){	attribute_used_context context;	context.rt_index = rt_index;	context.attno = attno;	context.sublevels_up = sublevels_up;	/*	 * Must be prepared to start with a Query or a bare expression tree;	 * if it's a Query, we don't want to increment sublevels_up.	 */	return query_or_expression_tree_walker(node,										   attribute_used_walker,										   (void *) &context,										   0);}/* * If the given Query is an INSERT ... SELECT construct, extract and * return the sub-Query node that represents the SELECT part.  Otherwise * return the given Query. * * If subquery_ptr is not NULL, then *subquery_ptr is set to the location * of the link to the SELECT subquery inside parsetree, or NULL if not an * INSERT ... SELECT. * * This is a hack needed because transformations on INSERT ... SELECTs that * appear in rule actions should be applied to the source SELECT, not to the * INSERT part.  Perhaps this can be cleaned up with redesigned querytrees. */Query *getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr){	Query	   *selectquery;	RangeTblEntry *selectrte;	RangeTblRef *rtr;	if (subquery_ptr)		*subquery_ptr = NULL;	if (parsetree == NULL)		return parsetree;	if (parsetree->commandType != CMD_INSERT)		return parsetree;	/*	 * Currently, this is ONLY applied to rule-action queries, and so we	 * expect to find the *OLD* and *NEW* placeholder entries in the given	 * query.  If they're not there, it must be an INSERT/SELECT in which	 * they've been pushed down to the SELECT.	 */	if (length(parsetree->rtable) >= 2 &&	 strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,			"*OLD*") == 0 &&	 strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,			"*NEW*") == 0)		return parsetree;	Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));	if (length(parsetree->jointree->fromlist) != 1)		elog(ERROR, "expected to find SELECT subquery");	rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);	Assert(IsA(rtr, RangeTblRef));	selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);	selectquery = selectrte->subquery;	if (!(selectquery && IsA(selectquery, Query) &&		  selectquery->commandType == CMD_SELECT))		elog(ERROR, "expected to find SELECT subquery");	if (length(selectquery->rtable) >= 2 &&	strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,		   "*OLD*") == 0 &&	strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,		   "*NEW*") == 0)	{		if (subquery_ptr)			*subquery_ptr = &(selectrte->subquery);		return selectquery;	}	elog(ERROR, "could not find rule placeholders");	return NULL;				/* not reached */}/* * Add the given qualifier condition to the query's WHERE clause */voidAddQual(Query *parsetree, Node *qual){	Node	   *copy;	if (qual == NULL)		return;	if (parsetree->commandType == CMD_UTILITY)	{		/*		 * There's noplace to put the qual on a utility statement.		 *		 * If it's a NOTIFY, silently ignore the qual; this means that the		 * NOTIFY will execute, whether or not there are any qualifying		 * rows. While clearly wrong, this is much more useful than		 * refusing to execute the rule at all, and extra NOTIFY events		 * are harmless for typical uses of NOTIFY.		 *		 * If it isn't a NOTIFY, error out, since unconditional execution of		 * other utility stmts is unlikely to be wanted.  (This case is		 * not currently allowed anyway, but keep the test for safety.)		 */		if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))			return;		else			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("conditional utility statements are not implemented")));	}	if (parsetree->setOperations != NULL)	{		/*		 * There's noplace to put the qual on a setop statement, either.		 * (This could be fixed, but right now the planner simply ignores		 * any qual condition on a setop query.)		 */		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));	}	/* INTERSECT want's the original, but we need to copy - Jan */	copy = copyObject(qual);	parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,											   copy);	/*	 * Make sure query is marked correctly if added qual has sublinks or	 * aggregates (not sure it can ever have aggs, but sublinks	 * definitely).  Need not search qual when query is already marked.	 */	if (!parsetree->hasAggs)		parsetree->hasAggs = checkExprHasAggs(copy);	if (!parsetree->hasSubLinks)		parsetree->hasSubLinks = checkExprHasSubLink(copy);}/* * Add the given havingQual to the one already contained in the parsetree * just as AddQual does for the normal 'where' qual */voidAddHavingQual(Query *parsetree, Node *havingQual){	Node	   *copy;	if (havingQual == NULL)		return;	if (parsetree->commandType == CMD_UTILITY)	{		/*		 * There's noplace to put the qual on a utility statement.		 *		 * See comments in AddQual for motivation.		 */		if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))			return;		else			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("conditional utility statements are not implemented")));	}	if (parsetree->setOperations != NULL)	{		/*		 * There's noplace to put the qual on a setop statement, either.		 * (This could be fixed, but right now the planner simply ignores		 * any qual condition on a setop query.)		 */		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));	}	/* INTERSECT want's the original, but we need to copy - Jan */	copy = copyObject(havingQual);	parsetree->havingQual = make_and_qual(parsetree->havingQual,										  copy);	/*	 * Make sure query is marked correctly if added qual has sublinks or	 * aggregates (not sure it can ever have aggs, but sublinks	 * definitely).  Need not search qual when query is already marked.	 */	if (!parsetree->hasAggs)		parsetree->hasAggs = checkExprHasAggs(copy);	if (!parsetree->hasSubLinks)		parsetree->hasSubLinks = checkExprHasSubLink(copy);}/* * Invert the given clause and add it to the WHERE qualifications of the * given querytree.  Inversion means "x IS NOT TRUE", not just "NOT x", * else we will do the wrong thing when x evaluates to NULL. */voidAddInvertedQual(Query *parsetree, Node *qual){	BooleanTest *invqual;	if (qual == NULL)		return;	/* Need not copy input qual, because AddQual will... */	invqual = makeNode(BooleanTest);	invqual->arg = (Expr *) qual;	invqual->booltesttype = IS_NOT_TRUE;	AddQual(parsetree, (Node *) invqual);}/* * ResolveNew - replace Vars with corresponding items from a targetlist * * Vars matching target_varno and sublevels_up are replaced by the * entry with matching resno from targetlist, if there is one. * If not, we either change the unmatched Var's varno to update_varno * (when event == CMD_UPDATE) or replace it with a constant NULL. * * Note: the business with inserted_sublink is needed to update hasSubLinks * in subqueries when the replacement adds a subquery inside a subquery. * Messy, isn't it?  We do not need to do similar pushups for hasAggs, * because it isn't possible for this transformation to insert a level-zero * aggregate reference into a subquery --- it could only insert outer aggs. */typedef struct{	int			target_varno;	int			sublevels_up;	List	   *targetlist;	int			event;	int			update_varno;	bool		inserted_sublink;} ResolveNew_context;static Node *ResolveNew_mutator(Node *node, ResolveNew_context *context){	if (node == NULL)		return NULL;	if (IsA(node, Var))	{		Var		   *var = (Var *) node;		int			this_varno = (int) var->varno;		int			this_varlevelsup = (int) var->varlevelsup;		if (this_varno == context->target_varno &&			this_varlevelsup == context->sublevels_up)		{			TargetEntry *tle;			/* band-aid: don't do the wrong thing with a whole-tuple Var */			if (var->varattno == InvalidAttrNumber)				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("cannot handle whole-row reference")));			tle = get_tle_by_resno(context->targetlist, var->varattno);			if (tle == NULL)			{				if (context->event == CMD_UPDATE)				{					/* For update, just change unmatched var's varno */					var = (Var *) copyObject(node);					var->varno = context->update_varno;					var->varnoold = context->update_varno;					return (Node *) var;				}				else				{					/* Otherwise replace unmatched var with a null */					return (Node *) makeNullConst(var->vartype);				}			}			else			{				/* Make a copy of the tlist item to return */				Node	   *n = copyObject(tle->expr);				/* Adjust varlevelsup if tlist item is from higher query */				if (this_varlevelsup > 0)					IncrementVarSublevelsUp(n, this_varlevelsup, 0);				/* Report it if we are adding a sublink to query */				if (!context->inserted_sublink)					context->inserted_sublink = checkExprHasSubLink(n);				return n;			}		}		/* otherwise fall through to copy the var normally */	}	if (IsA(node, Query))	{		/* Recurse into RTE subquery or not-yet-planned sublink subquery */		Query	   *newnode;		bool		save_inserted_sublink;		context->sublevels_up++;		save_inserted_sublink = context->inserted_sublink;		context->inserted_sublink = false;		newnode = query_tree_mutator((Query *) node,									 ResolveNew_mutator,									 (void *) context,									 0);		newnode->hasSubLinks |= context->inserted_sublink;		context->inserted_sublink = save_inserted_sublink;		context->sublevels_up--;		return (Node *) newnode;	}	return expression_tree_mutator(node, ResolveNew_mutator,								   (void *) context);}Node *ResolveNew(Node *node, int target_varno, int sublevels_up,		   List *targetlist, int event, int update_varno){	ResolveNew_context context;	context.target_varno = target_varno;	context.sublevels_up = sublevels_up;	context.targetlist = targetlist;	context.event = event;	context.update_varno = update_varno;	context.inserted_sublink = false;	/*	 * Must be prepared to start with a Query or a bare expression tree;	 * if it's a Query, we don't want to increment sublevels_up.	 */	return query_or_expression_tree_mutator(node,											ResolveNew_mutator,											(void *) &context,											0);}

⌨️ 快捷键说明

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