prepunion.c

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

C
1,422
字号
/*------------------------------------------------------------------------- * * prepunion.c *	  Routines to plan set-operation queries.  The filename is a leftover *	  from a time when only UNIONs were implemented. * * There are two code paths in the planner for set-operation queries. * If a subquery consists entirely of simple UNION ALL operations, it * is converted into an "append relation".	Otherwise, it is handled * by the general code in this module (plan_set_operations and its * subroutines).  There is some support code here for the append-relation * case, but most of the heavy lifting for that is done elsewhere, * notably in prepjointree.c and allpaths.c. * * There is also some code here to support planning of queries that use * inheritance (SELECT FROM foo*).	Inheritance trees are converted into * append relations, and thenceforth share code with the UNION ALL case. * * * 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/prepunion.c,v 1.146 2008/01/01 19:45:50 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/namespace.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/plancat.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/prep.h"#include "optimizer/tlist.h"#include "parser/parse_clause.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parsetree.h"#include "utils/lsyscache.h"static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,					   double tuple_fraction,					   List *colTypes, bool junkOK,					   int flag, List *refnames_tlist,					   List **sortClauses);static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root,					double tuple_fraction,					List *refnames_tlist, List **sortClauses);static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,					   List *refnames_tlist, List **sortClauses);static List *recurse_union_children(Node *setOp, PlannerInfo *root,					   double tuple_fraction,					   SetOperationStmt *top_union,					   List *refnames_tlist);static List *generate_setop_tlist(List *colTypes, int flag,					 Index varno,					 bool hack_constants,					 List *input_tlist,					 List *refnames_tlist);static List *generate_append_tlist(List *colTypes, bool flag,					  List *input_plans,					  List *refnames_tlist);static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,						 Index rti);static void make_inh_translation_lists(Relation oldrelation,						   Relation newrelation,						   Index newvarno,						   List **col_mappings,						   List **translated_vars);static Node *adjust_appendrel_attrs_mutator(Node *node,							   AppendRelInfo *context);static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);static List *adjust_inherited_tlist(List *tlist,					   AppendRelInfo *context);/* * plan_set_operations * *	  Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT) * * This routine only deals with the setOperations tree of the given query. * Any top-level ORDER BY requested in root->parse->sortClause will be added * when we return to grouping_planner. * * tuple_fraction is the fraction of tuples we expect will be retrieved. * tuple_fraction is interpreted as for grouping_planner(); in particular, * zero means "all the tuples will be fetched".  Any LIMIT present at the * top level has already been factored into tuple_fraction. * * *sortClauses is an output argument: it is set to a list of SortClauses * representing the result ordering of the topmost set operation. */Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction,					List **sortClauses){	Query	   *parse = root->parse;	SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;	Node	   *node;	Query	   *leftmostQuery;	Assert(topop && IsA(topop, SetOperationStmt));	/* check for unsupported stuff */	Assert(parse->jointree->fromlist == NIL);	Assert(parse->jointree->quals == NULL);	Assert(parse->groupClause == NIL);	Assert(parse->havingQual == NULL);	Assert(parse->distinctClause == NIL);	/*	 * Find the leftmost component Query.  We need to use its column names for	 * all generated tlists (else SELECT INTO won't work right).	 */	node = topop->larg;	while (node && IsA(node, SetOperationStmt))		node = ((SetOperationStmt *) node)->larg;	Assert(node && IsA(node, RangeTblRef));	leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,							 parse->rtable)->subquery;	Assert(leftmostQuery != NULL);	/*	 * Recurse on setOperations tree to generate plans for set ops. The final	 * output plan should have just the column types shown as the output from	 * the top-level node, plus possibly resjunk working columns (we can rely	 * on upper-level nodes to deal with that).	 */	return recurse_set_operations((Node *) topop, root, tuple_fraction,								  topop->colTypes, true, -1,								  leftmostQuery->targetList,								  sortClauses);}/* * recurse_set_operations *	  Recursively handle one step in a tree of set operations * * tuple_fraction: fraction of tuples we expect to retrieve from node * colTypes: list of type OIDs of expected output columns * junkOK: if true, child resjunk columns may be left in the result * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from * *sortClauses: receives list of SortClauses for result plan, if any * * We don't have to care about typmods here: the only allowed difference * between set-op input and output typmods is input is a specific typmod * and output is -1, and that does not require a coercion. */static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,					   double tuple_fraction,					   List *colTypes, bool junkOK,					   int flag, List *refnames_tlist,					   List **sortClauses){	if (IsA(setOp, RangeTblRef))	{		RangeTblRef *rtr = (RangeTblRef *) setOp;		RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable);		Query	   *subquery = rte->subquery;		PlannerInfo *subroot;		Plan	   *subplan,				   *plan;		Assert(subquery != NULL);		/*		 * Generate plan for primitive subquery		 */		subplan = subquery_planner(root->glob, subquery,								   root->query_level + 1,								   tuple_fraction,								   &subroot);		/*		 * Add a SubqueryScan with the caller-requested targetlist		 */		plan = (Plan *)			make_subqueryscan(generate_setop_tlist(colTypes, flag,												   rtr->rtindex,												   true,												   subplan->targetlist,												   refnames_tlist),							  NIL,							  rtr->rtindex,							  subplan,							  subroot->parse->rtable);		/*		 * We don't bother to determine the subquery's output ordering since		 * it won't be reflected in the set-op result anyhow.		 */		*sortClauses = NIL;		return plan;	}	else if (IsA(setOp, SetOperationStmt))	{		SetOperationStmt *op = (SetOperationStmt *) setOp;		Plan	   *plan;		/* UNIONs are much different from INTERSECT/EXCEPT */		if (op->op == SETOP_UNION)			plan = generate_union_plan(op, root, tuple_fraction,									   refnames_tlist,									   sortClauses);		else			plan = generate_nonunion_plan(op, root,										  refnames_tlist,										  sortClauses);		/*		 * If necessary, add a Result node to project the caller-requested		 * output columns.		 *		 * XXX you don't really want to know about this: setrefs.c will apply		 * fix_upper_expr() to the Result node's tlist. This would fail if the		 * Vars generated by generate_setop_tlist() were not exactly equal()		 * to the corresponding tlist entries of the subplan. However, since		 * the subplan was generated by generate_union_plan() or		 * generate_nonunion_plan(), and hence its tlist was generated by		 * generate_append_tlist(), this will work.  We just tell		 * generate_setop_tlist() to use varno 0.		 */		if (flag >= 0 ||			!tlist_same_datatypes(plan->targetlist, colTypes, junkOK))		{			plan = (Plan *)				make_result(root,							generate_setop_tlist(colTypes, flag,												 0,												 false,												 plan->targetlist,												 refnames_tlist),							NULL,							plan);		}		return plan;	}	else	{		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(setOp));		return NULL;			/* keep compiler quiet */	}}/* * Generate plan for a UNION or UNION ALL node */static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root,					double tuple_fraction,					List *refnames_tlist,					List **sortClauses){	List	   *planlist;	List	   *tlist;	Plan	   *plan;	/*	 * If plain UNION, tell children to fetch all tuples.	 *	 * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to	 * each arm of the UNION ALL.  One could make a case for reducing the	 * tuple fraction for later arms (discounting by the expected size of the	 * earlier arms' results) but it seems not worth the trouble. The normal	 * case where tuple_fraction isn't already zero is a LIMIT at top level,	 * and passing it down as-is is usually enough to get the desired result	 * of preferring fast-start plans.	 */	if (!op->all)		tuple_fraction = 0.0;	/*	 * If any of my children are identical UNION nodes (same op, all-flag, and	 * colTypes) then they can be merged into this node so that we generate	 * only one Append and Sort for the lot.  Recurse to find such nodes and	 * compute their children's plans.	 */	planlist = list_concat(recurse_union_children(op->larg, root,												  tuple_fraction,												  op, refnames_tlist),						   recurse_union_children(op->rarg, root,												  tuple_fraction,												  op, refnames_tlist));	/*	 * Generate tlist for Append plan node.	 *	 * The tlist for an Append plan isn't important as far as the Append is	 * concerned, but we must make it look real anyway for the benefit of the	 * next plan level up.	 */	tlist = generate_append_tlist(op->colTypes, false,								  planlist, refnames_tlist);	/*	 * Append the child results together.	 */	plan = (Plan *) make_append(planlist, false, tlist);	/*	 * For UNION ALL, we just need the Append plan.  For UNION, need to add	 * Sort and Unique nodes to produce unique output.	 */	if (!op->all)	{		List	   *sortList;		sortList = addAllTargetsToSortList(NULL, NIL, tlist, false);		if (sortList)		{			plan = (Plan *) make_sort_from_sortclauses(root, sortList, plan);			plan = (Plan *) make_unique(plan, sortList);		}		*sortClauses = sortList;	}	else		*sortClauses = NIL;	return plan;}/* * Generate plan for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node */static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,					   List *refnames_tlist,					   List **sortClauses){	Plan	   *lplan,			   *rplan,			   *plan;	List	   *tlist,			   *sortList,			   *planlist,			   *child_sortclauses;	SetOpCmd	cmd;	/* Recurse on children, ensuring their outputs are marked */	lplan = recurse_set_operations(op->larg, root,								   0.0 /* all tuples needed */ ,								   op->colTypes, false, 0,								   refnames_tlist,								   &child_sortclauses);	rplan = recurse_set_operations(op->rarg, root,								   0.0 /* all tuples needed */ ,								   op->colTypes, false, 1,								   refnames_tlist,								   &child_sortclauses);	planlist = list_make2(lplan, rplan);	/*	 * Generate tlist for Append plan node.	 *	 * The tlist for an Append plan isn't important as far as the Append is	 * concerned, but we must make it look real anyway for the benefit of the	 * next plan level up.	In fact, it has to be real enough that the flag	 * column is shown as a variable not a constant, else setrefs.c will get	 * confused.	 */	tlist = generate_append_tlist(op->colTypes, true,								  planlist, refnames_tlist);	/*	 * Append the child results together.	 */	plan = (Plan *) make_append(planlist, false, tlist);	/*	 * Sort the child results, then add a SetOp plan node to generate the	 * correct output.	 */	sortList = addAllTargetsToSortList(NULL, NIL, tlist, false);	if (sortList == NIL)		/* nothing to sort on? */	{		*sortClauses = NIL;		return plan;	}	plan = (Plan *) make_sort_from_sortclauses(root, sortList, plan);	switch (op->op)	{		case SETOP_INTERSECT:			cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;			break;		case SETOP_EXCEPT:			cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;			break;		default:			elog(ERROR, "unrecognized set op: %d",				 (int) op->op);			cmd = SETOPCMD_INTERSECT;	/* keep compiler quiet */			break;	}	plan = (Plan *) make_setop(cmd, plan, sortList, list_length(op->colTypes) + 1);	*sortClauses = sortList;	return plan;}/* * Pull up children of a UNION node that are identically-propertied UNIONs. * * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct * output rows will be lost anyway. */static List *recurse_union_children(Node *setOp, PlannerInfo *root,					   double tuple_fraction,					   SetOperationStmt *top_union,					   List *refnames_tlist){	List	   *child_sortclauses;	if (IsA(setOp, SetOperationStmt))	{		SetOperationStmt *op = (SetOperationStmt *) setOp;		if (op->op == top_union->op &&			(op->all == top_union->all || op->all) &&			equal(op->colTypes, top_union->colTypes))		{			/* Same UNION, so fold children into parent's subplan list */			return list_concat(recurse_union_children(op->larg, root,													  tuple_fraction,													  top_union,													  refnames_tlist),							   recurse_union_children(op->rarg, root,													  tuple_fraction,													  top_union,													  refnames_tlist));		}	}	/*	 * Not same, so plan this child separately.	 *	 * Note we disallow any resjunk columns in child results.  This is	 * necessary since the Append node that implements the union won't do any	 * projection, and upper levels will get confused if some of our output	 * tuples have junk and some don't.  This case only arises when we have an	 * EXCEPT or INTERSECT as child, else there won't be resjunk anyway.	 */	return list_make1(recurse_set_operations(setOp, root,											 tuple_fraction,											 top_union->colTypes, false,											 -1, refnames_tlist,											 &child_sortclauses));}/* * Generate targetlist for a set-operation plan node * * colTypes: column datatypes for non-junk columns * flag: -1 if no flag column needed, 0 or 1 to create a const flag column * varno: varno to use in generated Vars * hack_constants: true to copy up constants (see comments in code) * input_tlist: targetlist of this node's input node * refnames_tlist: targetlist to take column names from

⌨️ 快捷键说明

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