prepjointree.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,329 行 · 第 1/3 页

C
1,329
字号
/*------------------------------------------------------------------------- * * prepjointree.c *	  Planner preprocessing for subqueries and join tree manipulation. * * NOTE: the intended sequence for invoking these operations is *		pull_up_IN_clauses *		pull_up_subqueries *		do expression preprocessing (including flattening JOIN alias vars) *		reduce_outer_joins * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.49.2.1 2008/08/14 20:31:59 heikki Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "optimizer/subselect.h"#include "optimizer/tlist.h"#include "optimizer/var.h"#include "parser/parse_expr.h"#include "parser/parsetree.h"#include "rewrite/rewriteManip.h"typedef struct reduce_outer_joins_state{	Relids		relids;			/* base relids within this subtree */	bool		contains_outer; /* does subtree contain outer join(s)? */	List	   *sub_states;		/* List of states for subtree components */} reduce_outer_joins_state;static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,						RangeTblEntry *rte,						bool below_outer_join,						bool append_rel_member);static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,						 RangeTblEntry *rte);static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,						  int parentRTindex, Query *setOpQuery,						  int childRToffset);static void make_setop_translation_lists(Query *query,							 Index newvarno,							 List **col_mappings, List **translated_vars);static bool is_simple_subquery(Query *subquery);static bool is_simple_union_all(Query *subquery);static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,							List *colTypes);static bool has_nullable_targetlist(Query *subquery);static bool is_safe_append_member(Query *subquery);static void resolvenew_in_jointree(Node *jtnode, int varno,					   RangeTblEntry *rte, List *subtlist);static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);static void reduce_outer_joins_pass2(Node *jtnode,						 reduce_outer_joins_state *state,						 PlannerInfo *root,						 Relids nonnullable_rels);static void fix_in_clause_relids(List *in_info_list, int varno,					 Relids subrelids);static void fix_append_rel_relids(List *append_rel_list, int varno,					  Relids subrelids);static Node *find_jointree_node_for_rel(Node *jtnode, int relid);/* * pull_up_IN_clauses *		Attempt to pull up top-level IN clauses to be treated like joins. * * A clause "foo IN (sub-SELECT)" appearing at the top level of WHERE can * be processed by pulling the sub-SELECT up to become a rangetable entry * and handling the implied equality comparisons as join operators (with * special join rules). * This optimization *only* works at the top level of WHERE, because * it cannot distinguish whether the IN ought to return FALSE or NULL in * cases involving NULL inputs.  This routine searches for such clauses * and does the necessary parsetree transformations if any are found. * * This routine has to run before preprocess_expression(), so the WHERE * clause is not yet reduced to implicit-AND format.  That means we need * to recursively search through explicit AND clauses, which are * probably only binary ANDs.  We stop as soon as we hit a non-AND item. * * Returns the possibly-modified version of the given qual-tree node. */Node *pull_up_IN_clauses(PlannerInfo *root, Node *node){	if (node == NULL)		return NULL;	if (IsA(node, SubLink))	{		SubLink    *sublink = (SubLink *) node;		Node	   *subst;		/* Is it a convertible IN clause?  If not, return it as-is */		subst = convert_IN_to_join(root, sublink);		if (subst == NULL)			return node;		return subst;	}	if (and_clause(node))	{		List	   *newclauses = NIL;		ListCell   *l;		foreach(l, ((BoolExpr *) node)->args)		{			Node	   *oldclause = (Node *) lfirst(l);			newclauses = lappend(newclauses,								 pull_up_IN_clauses(root, oldclause));		}		return (Node *) make_andclause(newclauses);	}	/* Stop if not an AND */	return node;}/* * pull_up_subqueries *		Look for subqueries in the rangetable that can be pulled up into *		the parent query.  If the subquery has no special features like *		grouping/aggregation then we can merge it into the parent's jointree. *		Also, subqueries that are simple UNION ALL structures can be *		converted into "append relations". * * below_outer_join is true if this jointree node is within the nullable * side of an outer join.  This restricts what we can do. * * append_rel_member is true if we are looking at a member subquery of * an append relation.	This puts some different restrictions on what * we can do. * * A tricky aspect of this code is that if we pull up a subquery we have * to replace Vars that reference the subquery's outputs throughout the * parent query, including quals attached to jointree nodes above the one * we are currently processing!  We handle this by being careful not to * change the jointree structure while recursing: no nodes other than * subquery RangeTblRef entries will be replaced.  Also, we can't turn * ResolveNew loose on the whole jointree, because it'll return a mutated * copy of the tree; we have to invoke it just on the quals, instead. */Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,				   bool below_outer_join, bool append_rel_member){	if (jtnode == NULL)		return NULL;	if (IsA(jtnode, RangeTblRef))	{		int			varno = ((RangeTblRef *) jtnode)->rtindex;		RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);		/*		 * Is this a subquery RTE, and if so, is the subquery simple enough to		 * pull up?  (If not, do nothing at this node.)		 *		 * If we are inside an outer join, only pull up subqueries whose		 * targetlists are nullable --- otherwise substituting their tlist		 * entries for upper Var references would do the wrong thing (the		 * results wouldn't become NULL when they're supposed to).		 *		 * XXX This could be improved by generating pseudo-variables for such		 * expressions; we'd have to figure out how to get the pseudo-		 * variables evaluated at the right place in the modified plan tree.		 * Fix it someday.		 *		 * If we are looking at an append-relation member, we can't pull it up		 * unless is_safe_append_member says so.		 */		if (rte->rtekind == RTE_SUBQUERY &&			is_simple_subquery(rte->subquery) &&			(!below_outer_join || has_nullable_targetlist(rte->subquery)) &&			(!append_rel_member || is_safe_append_member(rte->subquery)))			return pull_up_simple_subquery(root, jtnode, rte,										   below_outer_join,										   append_rel_member);		/*		 * Alternatively, is it a simple UNION ALL subquery?  If so, flatten		 * into an "append relation".  We can do this regardless of		 * nullability considerations since this transformation does not		 * result in propagating non-Var expressions into upper levels of the		 * query.		 *		 * It's also safe to do this regardless of whether this query is		 * itself an appendrel member.	(If you're thinking we should try to		 * flatten the two levels of appendrel together, you're right; but we		 * handle that in set_append_rel_pathlist, not here.)		 */		if (rte->rtekind == RTE_SUBQUERY &&			is_simple_union_all(rte->subquery))			return pull_up_simple_union_all(root, jtnode, rte);	}	else if (IsA(jtnode, FromExpr))	{		FromExpr   *f = (FromExpr *) jtnode;		ListCell   *l;		Assert(!append_rel_member);		foreach(l, f->fromlist)			lfirst(l) = pull_up_subqueries(root, lfirst(l),										   below_outer_join, false);	}	else if (IsA(jtnode, JoinExpr))	{		JoinExpr   *j = (JoinExpr *) jtnode;		Assert(!append_rel_member);		/* Recurse, being careful to tell myself when inside outer join */		switch (j->jointype)		{			case JOIN_INNER:				j->larg = pull_up_subqueries(root, j->larg,											 below_outer_join, false);				j->rarg = pull_up_subqueries(root, j->rarg,											 below_outer_join, false);				break;			case JOIN_LEFT:				j->larg = pull_up_subqueries(root, j->larg,											 below_outer_join, false);				j->rarg = pull_up_subqueries(root, j->rarg,											 true, false);				break;			case JOIN_FULL:				j->larg = pull_up_subqueries(root, j->larg,											 true, false);				j->rarg = pull_up_subqueries(root, j->rarg,											 true, false);				break;			case JOIN_RIGHT:				j->larg = pull_up_subqueries(root, j->larg,											 true, false);				j->rarg = pull_up_subqueries(root, j->rarg,											 below_outer_join, false);				break;			default:				elog(ERROR, "unrecognized join type: %d",					 (int) j->jointype);				break;		}	}	else		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(jtnode));	return jtnode;}/* * pull_up_simple_subquery *		Attempt to pull up a single simple subquery. * * jtnode is a RangeTblRef that has been tentatively identified as a simple * subquery by pull_up_subqueries.	We return the replacement jointree node, * or jtnode itself if we determine that the subquery can't be pulled up after * all. */static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,						bool below_outer_join, bool append_rel_member){	Query	   *parse = root->parse;	int			varno = ((RangeTblRef *) jtnode)->rtindex;	Query	   *subquery;	PlannerInfo *subroot;	int			rtoffset;	List	   *subtlist;	ListCell   *rt;	/*	 * Need a modifiable copy of the subquery to hack on.  Even if we didn't	 * sometimes choose not to pull up below, we must do this to avoid	 * problems if the same subquery is referenced from multiple jointree	 * items (which can't happen normally, but might after rule rewriting).	 */	subquery = copyObject(rte->subquery);	/*	 * Create a PlannerInfo data structure for this subquery.	 *	 * NOTE: the next few steps should match the first processing in	 * subquery_planner().	Can we refactor to avoid code duplication, or	 * would that just make things uglier?	 */	subroot = makeNode(PlannerInfo);	subroot->parse = subquery;	subroot->glob = root->glob;	subroot->query_level = root->query_level;	subroot->planner_cxt = CurrentMemoryContext;	subroot->init_plans = NIL;	subroot->in_info_list = NIL;	subroot->append_rel_list = NIL;	/*	 * Pull up any IN clauses within the subquery's WHERE, so that we don't	 * leave unoptimized INs behind.	 */	if (subquery->hasSubLinks)		subquery->jointree->quals = pull_up_IN_clauses(subroot,												  subquery->jointree->quals);	/*	 * Recursively pull up the subquery's subqueries, so that	 * pull_up_subqueries' processing is complete for its jointree and	 * rangetable.	 *	 * Note: below_outer_join = false is correct here even if we are within an	 * outer join in the upper query; the lower query starts with a clean	 * slate for outer-join semantics.	Likewise, we say we aren't handling an	 * appendrel member.	 */	subquery->jointree = (FromExpr *)		pull_up_subqueries(subroot, (Node *) subquery->jointree, false, false);	/*	 * Now we must recheck whether the subquery is still simple enough to pull	 * up.	If not, abandon processing it.	 *	 * We don't really need to recheck all the conditions involved, but it's	 * easier just to keep this "if" looking the same as the one in	 * pull_up_subqueries.	 */	if (is_simple_subquery(subquery) &&		(!below_outer_join || has_nullable_targetlist(subquery)) &&		(!append_rel_member || is_safe_append_member(subquery)))	{		/* good to go */	}	else	{		/*		 * Give up, return unmodified RangeTblRef.		 *		 * Note: The work we just did will be redone when the subquery gets		 * planned on its own.	Perhaps we could avoid that by storing the		 * modified subquery back into the rangetable, but I'm not gonna risk		 * it now.		 */		return jtnode;	}	/*	 * Adjust level-0 varnos in subquery so that we can append its rangetable	 * to upper query's.  We have to fix the subquery's in_info_list and	 * append_rel_list, as well.	 */	rtoffset = list_length(parse->rtable);	OffsetVarNodes((Node *) subquery, rtoffset, 0);	OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0);	OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);	/*	 * Upper-level vars in subquery are now one level closer to their parent	 * than before.	 */	IncrementVarSublevelsUp((Node *) subquery, -1, 1);	IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1);	IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);	/*	 * Replace all of the top query's references to the subquery's outputs	 * with copies of the adjusted subtlist items, being careful not to	 * replace any of the jointree structure. (This'd be a lot cleaner if we	 * could use query_tree_mutator.)	 */	subtlist = subquery->targetList;	parse->targetList = (List *)		ResolveNew((Node *) parse->targetList,				   varno, 0, rte,				   subtlist, CMD_SELECT, 0);	parse->returningList = (List *)		ResolveNew((Node *) parse->returningList,				   varno, 0, rte,				   subtlist, CMD_SELECT, 0);	resolvenew_in_jointree((Node *) parse->jointree, varno,						   rte, subtlist);	Assert(parse->setOperations == NULL);	parse->havingQual =		ResolveNew(parse->havingQual,				   varno, 0, rte,				   subtlist, CMD_SELECT, 0);	root->in_info_list = (List *)		ResolveNew((Node *) root->in_info_list,				   varno, 0, rte,				   subtlist, CMD_SELECT, 0);	root->append_rel_list = (List *)		ResolveNew((Node *) root->append_rel_list,				   varno, 0, rte,				   subtlist, CMD_SELECT, 0);	foreach(rt, parse->rtable)	{		RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);		if (otherrte->rtekind == RTE_JOIN)			otherrte->joinaliasvars = (List *)				ResolveNew((Node *) otherrte->joinaliasvars,						   varno, 0, rte,						   subtlist, CMD_SELECT, 0);	}	/*	 * Now append the adjusted rtable entries to upper query. (We hold off	 * until after fixing the upper rtable entries; no point in running that	 * code on the subquery ones too.)	 */	parse->rtable = list_concat(parse->rtable, subquery->rtable);	/*	 * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes already	 * adjusted the marker rtindexes, so just concat the lists.)	 */	parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);	/*	 * We also have to fix the relid sets of any parent InClauseInfo nodes.	 * (This could perhaps be done by ResolveNew, but it would clutter that	 * routine's API unreasonably.)	 *	 * Likewise, relids appearing in AppendRelInfo nodes have to be fixed (but	 * we took care of their translated_vars lists above).	We already checked	 * that this won't require introducing multiple subrelids into the	 * single-slot AppendRelInfo structs.	 */	if (root->in_info_list || root->append_rel_list)	{		Relids		subrelids;		subrelids = get_relids_in_jointree((Node *) subquery->jointree);		fix_in_clause_relids(root->in_info_list, varno, subrelids);		fix_append_rel_relids(root->append_rel_list, varno, subrelids);	}	/*

⌨️ 快捷键说明

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