subselect.c

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

C
1,118
字号
/*------------------------------------------------------------------------- * * subselect.c *	  Planning routines for subselects and parameters. * * 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/optimizer/plan/subselect.c,v 1.83.2.2 2004/05/11 13:15:23 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "nodes/params.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/subselect.h"#include "optimizer/var.h"#include "parser/parsetree.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_relation.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"Index		PlannerQueryLevel;	/* level of current query */List	   *PlannerInitPlan;	/* init subplans for current query */List	   *PlannerParamList;	/* to keep track of cross-level Params */int			PlannerPlanId = 0;	/* to assign unique ID to subquery plans *//* * PlannerParamList keeps track of the PARAM_EXEC slots that we have decided * we need for the query.  At runtime these slots are used to pass values * either down into subqueries (for outer references in subqueries) or up out * of subqueries (for the results of a subplan).  The n'th entry in the list * (n counts from 0) corresponds to Param->paramid = n. * * Each ParamList item shows the absolute query level it is associated with, * where the outermost query is level 1 and nested subqueries have higher * numbers.  The item the parameter slot represents can be one of three kinds: * * A Var: the slot represents a variable of that level that must be passed * down because subqueries have outer references to it.  The varlevelsup * value in the Var will always be zero. * * An Aggref (with an expression tree representing its argument): the slot * represents an aggregate expression that is an outer reference for some * subquery.  The Aggref itself has agglevelsup = 0, and its argument tree * is adjusted to match in level. * * A Param: the slot holds the result of a subplan (it is a setParam item * for that subplan).  The absolute level shown for such items corresponds * to the parent query of the subplan. * * Note: we detect duplicate Var parameters and coalesce them into one slot, * but we do not do this for Aggref or Param slots. */typedef struct PlannerParamItem{	Node	   *item;			/* the Var, Aggref, or Param */	Index		abslevel;		/* its absolute query level */} PlannerParamItem;typedef struct finalize_primnode_context{	Bitmapset  *paramids;		/* Set of PARAM_EXEC paramids found */	Bitmapset  *outer_params;	/* Set of accessible outer paramids */} finalize_primnode_context;static List *convert_sublink_opers(List *lefthand, List *operOids,					  List *targetlist, int rtindex,					  List **righthandIds);static bool subplan_is_hashable(SubLink *slink, SubPlan *node);static Node *replace_correlation_vars_mutator(Node *node, void *context);static Node *process_sublinks_mutator(Node *node, bool *isTopQual);static Bitmapset *finalize_plan(Plan *plan, List *rtable,			  Bitmapset *outer_params,			  Bitmapset *valid_params);static bool finalize_primnode(Node *node, finalize_primnode_context *context);/* * Generate a Param node to replace the given Var, * which is expected to have varlevelsup > 0 (ie, it is not local). */static Param *replace_outer_var(Var *var){	Param	   *retval;	List	   *ppl;	PlannerParamItem *pitem;	Index		abslevel;	int			i;	Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);	abslevel = PlannerQueryLevel - var->varlevelsup;	/*	 * If there's already a PlannerParamList entry for this same Var, just	 * use it.	NOTE: in sufficiently complex querytrees, it is possible	 * for the same varno/abslevel to refer to different RTEs in different	 * parts of the parsetree, so that different fields might end up	 * sharing the same Param number.  As long as we check the vartype as	 * well, I believe that this sort of aliasing will cause no trouble.	 * The correct field should get stored into the Param slot at	 * execution in each part of the tree.	 *	 * We also need to demand a match on vartypmod.  This does not matter	 * for the Param itself, since those are not typmod-dependent, but it	 * does matter when make_subplan() instantiates a modified copy of the	 * Var for a subplan's args list.	 */	i = 0;	foreach(ppl, PlannerParamList)	{		pitem = (PlannerParamItem *) lfirst(ppl);		if (pitem->abslevel == abslevel && IsA(pitem->item, Var))		{			Var		   *pvar = (Var *) pitem->item;			if (pvar->varno == var->varno &&				pvar->varattno == var->varattno &&				pvar->vartype == var->vartype &&				pvar->vartypmod == var->vartypmod)				break;		}		i++;	}	if (!ppl)	{		/* Nope, so make a new one */		var = (Var *) copyObject(var);		var->varlevelsup = 0;		pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));		pitem->item = (Node *) var;		pitem->abslevel = abslevel;		PlannerParamList = lappend(PlannerParamList, pitem);		/* i is already the correct index for the new item */	}	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = (AttrNumber) i;	retval->paramtype = var->vartype;	return retval;}/* * Generate a Param node to replace the given Aggref * which is expected to have agglevelsup > 0 (ie, it is not local). */static Param *replace_outer_agg(Aggref *agg){	Param	   *retval;	PlannerParamItem *pitem;	Index		abslevel;	int			i;	Assert(agg->agglevelsup > 0 && agg->agglevelsup < PlannerQueryLevel);	abslevel = PlannerQueryLevel - agg->agglevelsup;	/*	 * It does not seem worthwhile to try to match duplicate outer aggs.	 * Just make a new slot every time.	 */	agg = (Aggref *) copyObject(agg);	IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);	Assert(agg->agglevelsup == 0);	pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));	pitem->item = (Node *) agg;	pitem->abslevel = abslevel;	PlannerParamList = lappend(PlannerParamList, pitem);	i = length(PlannerParamList) - 1;	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = (AttrNumber) i;	retval->paramtype = agg->aggtype;	return retval;}/* * Generate a new Param node that will not conflict with any other. * * This is used to allocate PARAM_EXEC slots for subplan outputs. * * paramtypmod is currently unused but might be wanted someday. */static Param *generate_new_param(Oid paramtype, int32 paramtypmod){	Param	   *retval;	PlannerParamItem *pitem;	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = (AttrNumber) length(PlannerParamList);	retval->paramtype = paramtype;	pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));	pitem->item = (Node *) retval;	pitem->abslevel = PlannerQueryLevel;	PlannerParamList = lappend(PlannerParamList, pitem);	return retval;}/* * Convert a bare SubLink (as created by the parser) into a SubPlan. * * We are given the raw SubLink and the already-processed lefthand argument * list (use this instead of the SubLink's own field).  We are also told if * this expression appears at top level of a WHERE/HAVING qual. * * The result is whatever we need to substitute in place of the SubLink * node in the executable expression.  This will be either the SubPlan * node (if we have to do the subplan as a subplan), or a Param node * representing the result of an InitPlan, or possibly an AND or OR tree * containing InitPlan Param nodes. */static Node *make_subplan(SubLink *slink, List *lefthand, bool isTopQual){	SubPlan    *node = makeNode(SubPlan);	Query	   *subquery = (Query *) (slink->subselect);	double		tuple_fraction;	Plan	   *plan;	Bitmapset  *tmpset;	int			paramid;	List	   *lst;	Node	   *result;	/*	 * Copy the source Query node.	This is a quick and dirty kluge to	 * resolve the fact that the parser can generate trees with multiple	 * links to the same sub-Query node, but the planner wants to scribble	 * on the Query. Try to clean this up when we do querytree redesign...	 */	subquery = (Query *) copyObject(subquery);	/*	 * For an EXISTS subplan, tell lower-level planner to expect that only	 * the first tuple will be retrieved.  For ALL and ANY subplans, we	 * will be able to stop evaluating if the test condition fails, so	 * very often not all the tuples will be retrieved; for lack of a	 * better idea, specify 50% retrieval.	For EXPR and MULTIEXPR	 * subplans, use default behavior (we're only expecting one row out,	 * anyway).	 *	 * NOTE: if you change these numbers, also change cost_qual_eval_walker()	 * in path/costsize.c.	 *	 * XXX If an ALL/ANY subplan is uncorrelated, we may decide to hash or	 * materialize its result below.  In that case it would've been better	 * to specify full retrieval.  At present, however, we can only detect	 * correlation or lack of it after we've made the subplan :-(. Perhaps	 * detection of correlation should be done as a separate step.	 * Meanwhile, we don't want to be too optimistic about the percentage	 * of tuples retrieved, for fear of selecting a plan that's bad for	 * the materialization case.	 */	if (slink->subLinkType == EXISTS_SUBLINK)		tuple_fraction = 1.0;	/* just like a LIMIT 1 */	else if (slink->subLinkType == ALL_SUBLINK ||			 slink->subLinkType == ANY_SUBLINK)		tuple_fraction = 0.5;	/* 50% */	else		tuple_fraction = 0.0;	/* default behavior */	/*	 * Generate the plan for the subquery.	 */	node->plan = plan = subquery_planner(subquery, tuple_fraction);	node->plan_id = PlannerPlanId++;	/* Assign unique ID to this										 * SubPlan */	node->rtable = subquery->rtable;	/*	 * Initialize other fields of the SubPlan node.	 */	node->subLinkType = slink->subLinkType;	node->useOr = slink->useOr;	node->exprs = NIL;	node->paramIds = NIL;	node->useHashTable = false;	/* At top level of a qual, can treat UNKNOWN the same as FALSE */	node->unknownEqFalse = isTopQual;	node->setParam = NIL;	node->parParam = NIL;	node->args = NIL;	/*	 * Make parParam list of params that current query level will pass to	 * this child plan.	 */	tmpset = bms_copy(plan->extParam);	while ((paramid = bms_first_member(tmpset)) >= 0)	{		PlannerParamItem *pitem = nth(paramid, PlannerParamList);		if (pitem->abslevel == PlannerQueryLevel)			node->parParam = lappendi(node->parParam, paramid);	}	bms_free(tmpset);	/*	 * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY,	 * or MULTIEXPR types can be used as initPlans.  For EXISTS, EXPR, or	 * ARRAY, we just produce a Param referring to the result of	 * evaluating the initPlan.  For MULTIEXPR, we must build an AND or	 * OR-clause of the individual comparison operators, using the	 * appropriate lefthand side expressions and Params for the initPlan's	 * target items.	 */	if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)	{		Param	   *prm;		prm = generate_new_param(BOOLOID, -1);		node->setParam = makeListi1(prm->paramid);		PlannerInitPlan = lappend(PlannerInitPlan, node);		result = (Node *) prm;	}	else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)	{		TargetEntry *te = lfirst(plan->targetlist);		Param	   *prm;		Assert(!te->resdom->resjunk);		prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);		node->setParam = makeListi1(prm->paramid);		PlannerInitPlan = lappend(PlannerInitPlan, node);		result = (Node *) prm;	}	else if (node->parParam == NIL && slink->subLinkType == ARRAY_SUBLINK)	{		TargetEntry *te = lfirst(plan->targetlist);		Oid			arraytype;		Param	   *prm;		Assert(!te->resdom->resjunk);		arraytype = get_array_type(te->resdom->restype);		if (!OidIsValid(arraytype))			elog(ERROR, "could not find array type for datatype %s",				 format_type_be(te->resdom->restype));		prm = generate_new_param(arraytype, -1);		node->setParam = makeListi1(prm->paramid);		PlannerInitPlan = lappend(PlannerInitPlan, node);

⌨️ 快捷键说明

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