prepjointree.c

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

C
1,329
字号
	 * And now add any subquery InClauseInfos and AppendRelInfos to our lists.	 */	root->in_info_list = list_concat(root->in_info_list,									 subroot->in_info_list);	root->append_rel_list = list_concat(root->append_rel_list,										subroot->append_rel_list);	/*	 * We don't have to do the equivalent bookkeeping for outer-join info,	 * because that hasn't been set up yet.	 */	Assert(root->oj_info_list == NIL);	Assert(subroot->oj_info_list == NIL);	/*	 * Miscellaneous housekeeping.	 */	parse->hasSubLinks |= subquery->hasSubLinks;	/* subquery won't be pulled up if it hasAggs, so no work there */	/*	 * Return the adjusted subquery jointree to replace the RangeTblRef entry	 * in parent's jointree.	 */	return (Node *) subquery->jointree;}/* * pull_up_simple_union_all *		Pull up a single simple UNION ALL subquery. * * jtnode is a RangeTblRef that has been identified as a simple UNION ALL * subquery by pull_up_subqueries.	We pull up the leaf subqueries and * build an "append relation" for the union set.  The result value is just * jtnode, since we don't actually need to change the query jointree. */static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte){	int			varno = ((RangeTblRef *) jtnode)->rtindex;	Query	   *subquery = rte->subquery;	int			rtoffset;	List	   *rtable;	/*	 * Append the subquery rtable entries to upper query.	 */	rtoffset = list_length(root->parse->rtable);	/*	 * Append child RTEs to parent rtable.	 *	 * Upper-level vars in subquery are now one level closer to their	 * parent than before.	We don't have to worry about offsetting	 * varnos, though, because any such vars must refer to stuff above the	 * level of the query we are pulling into.	 */	rtable = copyObject(subquery->rtable);	IncrementVarSublevelsUp_rtable(rtable, -1, 1);	root->parse->rtable = list_concat(root->parse->rtable, rtable);	/*	 * Recursively scan the subquery's setOperations tree and add	 * AppendRelInfo nodes for leaf subqueries to the parent's	 * append_rel_list.	 */	Assert(subquery->setOperations);	pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,							   rtoffset);	/*	 * Mark the parent as an append relation.	 */	rte->inh = true;	return jtnode;}/* * pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all * * Note that setOpQuery is the Query containing the setOp node, whose rtable * is where to look up the RTE if setOp is a RangeTblRef.  This is *not* the * same as root->parse, which is the top-level Query we are pulling up into. * * parentRTindex is the appendrel parent's index in root->parse->rtable. * * The child RTEs have already been copied to the parent. childRToffset * tells us where in the parent's range table they were copied. */static voidpull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,						   Query *setOpQuery, int childRToffset){	if (IsA(setOp, RangeTblRef))	{		RangeTblRef *rtr = (RangeTblRef *) setOp;		int			childRTindex;		AppendRelInfo *appinfo;		/*		 * Calculate the index in the parent's range table		 */		childRTindex = childRToffset + rtr->rtindex;		/*		 * Build a suitable AppendRelInfo, and attach to parent's list.		 */		appinfo = makeNode(AppendRelInfo);		appinfo->parent_relid = parentRTindex;		appinfo->child_relid = childRTindex;		appinfo->parent_reltype = InvalidOid;		appinfo->child_reltype = InvalidOid;		make_setop_translation_lists(setOpQuery, childRTindex,									 &appinfo->col_mappings,									 &appinfo->translated_vars);		appinfo->parent_reloid = InvalidOid;		root->append_rel_list = lappend(root->append_rel_list, appinfo);		/*		 * Recursively apply pull_up_subqueries to the new child RTE.  (We		 * must build the AppendRelInfo first, because this will modify it.)		 * Note that we can pass below_outer_join = false even if we're		 * actually under an outer join, because the child's expressions		 * aren't going to propagate up above the join.		 */		rtr = makeNode(RangeTblRef);		rtr->rtindex = childRTindex;		(void) pull_up_subqueries(root, (Node *) rtr, false, true);	}	else if (IsA(setOp, SetOperationStmt))	{		SetOperationStmt *op = (SetOperationStmt *) setOp;		/* Recurse to reach leaf queries */		pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,								   childRToffset);		pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,								   childRToffset);	}	else	{		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(setOp));	}}/* * make_setop_translation_lists *	  Build the lists of translations from parent Vars to child Vars for *	  a UNION ALL member.  We need both a column number mapping list *	  and a list of Vars representing the child columns. */static voidmake_setop_translation_lists(Query *query,							 Index newvarno,							 List **col_mappings, List **translated_vars){	List	   *numbers = NIL;	List	   *vars = NIL;	ListCell   *l;	foreach(l, query->targetList)	{		TargetEntry *tle = (TargetEntry *) lfirst(l);		if (tle->resjunk)			continue;		numbers = lappend_int(numbers, tle->resno);		vars = lappend(vars, makeVar(newvarno,									 tle->resno,									 exprType((Node *) tle->expr),									 exprTypmod((Node *) tle->expr),									 0));	}	*col_mappings = numbers;	*translated_vars = vars;}/* * is_simple_subquery *	  Check a subquery in the range table to see if it's simple enough *	  to pull up into the parent query. */static boolis_simple_subquery(Query *subquery){	/*	 * Let's just make sure it's a valid subselect ...	 */	if (!IsA(subquery, Query) ||		subquery->commandType != CMD_SELECT ||		subquery->utilityStmt != NULL ||		subquery->intoClause != NULL)		elog(ERROR, "subquery is bogus");	/*	 * Can't currently pull up a query with setops (unless it's simple UNION	 * ALL, which is handled by a different code path). Maybe after querytree	 * redesign...	 */	if (subquery->setOperations)		return false;	/*	 * Can't pull up a subquery involving grouping, aggregation, sorting, or	 * limiting.	 */	if (subquery->hasAggs ||		subquery->groupClause ||		subquery->havingQual ||		subquery->sortClause ||		subquery->distinctClause ||		subquery->limitOffset ||		subquery->limitCount)		return false;	/*	 * Don't pull up a subquery that has any set-returning functions in its	 * targetlist.	Otherwise we might well wind up inserting set-returning	 * functions into places where they mustn't go, such as quals of higher	 * queries.	 */	if (expression_returns_set((Node *) subquery->targetList))		return false;	/*	 * Don't pull up a subquery that has any volatile functions in its	 * targetlist.	Otherwise we might introduce multiple evaluations of these	 * functions, if they get copied to multiple places in the upper query,	 * leading to surprising results.	 */	if (contain_volatile_functions((Node *) subquery->targetList))		return false;	/*	 * Hack: don't try to pull up a subquery with an empty jointree.	 * query_planner() will correctly generate a Result plan for a jointree	 * that's totally empty, but I don't think the right things happen if an	 * empty FromExpr appears lower down in a jointree. Not worth working hard	 * on this, just to collapse SubqueryScan/Result into Result...	 */	if (subquery->jointree->fromlist == NIL)		return false;	return true;}/* * is_simple_union_all *	  Check a subquery to see if it's a simple UNION ALL. * * We require all the setops to be UNION ALL (no mixing) and there can't be * any datatype coercions involved, ie, all the leaf queries must emit the * same datatypes. */static boolis_simple_union_all(Query *subquery){	SetOperationStmt *topop;	/* Let's just make sure it's a valid subselect ... */	if (!IsA(subquery, Query) ||		subquery->commandType != CMD_SELECT ||		subquery->utilityStmt != NULL ||		subquery->intoClause != NULL)		elog(ERROR, "subquery is bogus");	/* Is it a set-operation query at all? */	topop = (SetOperationStmt *) subquery->setOperations;	if (!topop)		return false;	Assert(IsA(topop, SetOperationStmt));	/* Can't handle ORDER BY, LIMIT/OFFSET, or locking */	if (subquery->sortClause ||		subquery->limitOffset ||		subquery->limitCount ||		subquery->rowMarks)		return false;	/* Recursively check the tree of set operations */	return is_simple_union_all_recurse((Node *) topop, subquery,									   topop->colTypes);}static boolis_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes){	if (IsA(setOp, RangeTblRef))	{		RangeTblRef *rtr = (RangeTblRef *) setOp;		RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable);		Query	   *subquery = rte->subquery;		Assert(subquery != NULL);		/* Leaf nodes are OK if they match the toplevel column types */		/* We don't have to compare typmods here */		return tlist_same_datatypes(subquery->targetList, colTypes, true);	}	else if (IsA(setOp, SetOperationStmt))	{		SetOperationStmt *op = (SetOperationStmt *) setOp;		/* Must be UNION ALL */		if (op->op != SETOP_UNION || !op->all)			return false;		/* Recurse to check inputs */		return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) &&			is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes);	}	else	{		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(setOp));		return false;			/* keep compiler quiet */	}}/* * has_nullable_targetlist *	  Check a subquery in the range table to see if all the non-junk *	  targetlist items are simple variables or strict functions of simple *	  variables (and, hence, will correctly go to NULL when examined above *	  the point of an outer join). * * NOTE: it would be correct (and useful) to ignore output columns that aren't * actually referenced by the enclosing query ... but we do not have that * information available at this point. */static boolhas_nullable_targetlist(Query *subquery){	ListCell   *l;	foreach(l, subquery->targetList)	{		TargetEntry *tle = (TargetEntry *) lfirst(l);		/* ignore resjunk columns */		if (tle->resjunk)			continue;		/* Must contain a Var of current level */		if (!contain_vars_of_level((Node *) tle->expr, 0))			return false;		/* Must not contain any non-strict constructs */		if (contain_nonstrict_functions((Node *) tle->expr))			return false;		/* This one's OK, keep scanning */	}	return true;}/* * is_safe_append_member *	  Check a subquery that is a leaf of a UNION ALL appendrel to see if it's *	  safe to pull up. */static boolis_safe_append_member(Query *subquery){	FromExpr   *jtnode;	ListCell   *l;	/*	 * It's only safe to pull up the child if its jointree contains exactly	 * one RTE, else the AppendRelInfo data structure breaks. The one base RTE	 * could be buried in several levels of FromExpr, however.	 *	 * Also, the child can't have any WHERE quals because there's no place to	 * put them in an appendrel.  (This is a bit annoying...) If we didn't	 * need to check this, we'd just test whether get_relids_in_jointree()	 * yields a singleton set, to be more consistent with the coding of	 * fix_append_rel_relids().	 */	jtnode = subquery->jointree;	while (IsA(jtnode, FromExpr))	{		if (jtnode->quals != NULL)			return false;		if (list_length(jtnode->fromlist) != 1)			return false;		jtnode = linitial(jtnode->fromlist);	}	if (!IsA(jtnode, RangeTblRef))		return false;	/*	 * XXX For the moment we also have to insist that the subquery's tlist	 * includes only simple Vars.  This is pretty annoying, but fixing it	 * seems to require nontrivial changes --- mainly because joinrel tlists	 * are presently assumed to contain only Vars.	Perhaps a pseudo-variable	 * mechanism similar to the one speculated about in pull_up_subqueries'	 * comments would help?  FIXME someday.	 */	foreach(l, subquery->targetList)	{		TargetEntry *tle = (TargetEntry *) lfirst(l);		if (tle->resjunk)			continue;		if (!(tle->expr && IsA(tle->expr, Var)))			return false;	}	return true;}/* * Helper routine for pull_up_subqueries: do ResolveNew on every expression * in the jointree, without changing the jointree structure itself.  Ugly, * but there's no other way... */static voidresolvenew_in_jointree(Node *jtnode, int varno,					   RangeTblEntry *rte, List *subtlist){	if (jtnode == NULL)		return;	if (IsA(jtnode, RangeTblRef))	{		/* nothing to do here */	}	else if (IsA(jtnode, FromExpr))	{		FromExpr   *f = (FromExpr *) jtnode;		ListCell   *l;		foreach(l, f->fromlist)			resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);		f->quals = ResolveNew(f->quals,							  varno, 0, rte,							  subtlist, CMD_SELECT, 0);	}	else if (IsA(jtnode, JoinExpr))	{

⌨️ 快捷键说明

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