subselect.c

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

C
1,470
字号
/*------------------------------------------------------------------------- * * subselect.c *	  Planning routines for subselects and parameters. * * 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/plan/subselect.c,v 1.129.2.2 2008/07/10 01:17:36 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "nodes/makefuncs.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/parse_expr.h"#include "parser/parse_relation.h"#include "parser/parsetree.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"typedef struct convert_testexpr_context{	PlannerInfo *root;	List	   *subst_nodes;	/* Nodes to substitute for Params */} convert_testexpr_context;typedef struct process_sublinks_context{	PlannerInfo *root;	bool		isTopQual;} process_sublinks_context;typedef struct finalize_primnode_context{	PlannerInfo *root;	Bitmapset  *paramids;		/* Non-local PARAM_EXEC paramids found */} finalize_primnode_context;static List *generate_subquery_params(PlannerInfo *root, List *tlist,									  List **paramIds);static List *generate_subquery_vars(PlannerInfo *root, List *tlist,									Index varno);static Node *convert_testexpr(PlannerInfo *root,				 Node *testexpr,				 List *subst_nodes);static Node *convert_testexpr_mutator(Node *node,						 convert_testexpr_context *context);static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan);static bool hash_ok_operator(OpExpr *expr);static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);static Node *process_sublinks_mutator(Node *node,						 process_sublinks_context *context);static Bitmapset *finalize_plan(PlannerInfo *root,			  Plan *plan,			  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(PlannerInfo *root, Var *var){	Param	   *retval;	ListCell   *ppl;	PlannerParamItem *pitem;	Index		abslevel;	int			i;	Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);	abslevel = root->query_level - var->varlevelsup;	/*	 * If there's already a paramlist 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, root->glob->paramlist)	{		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 = makeNode(PlannerParamItem);		pitem->item = (Node *) var;		pitem->abslevel = abslevel;		root->glob->paramlist = lappend(root->glob->paramlist, pitem);		/* i is already the correct index for the new item */	}	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = i;	retval->paramtype = var->vartype;	retval->paramtypmod = var->vartypmod;	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(PlannerInfo *root, Aggref *agg){	Param	   *retval;	PlannerParamItem *pitem;	Index		abslevel;	int			i;	Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);	abslevel = root->query_level - 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 = makeNode(PlannerParamItem);	pitem->item = (Node *) agg;	pitem->abslevel = abslevel;	root->glob->paramlist = lappend(root->glob->paramlist, pitem);	i = list_length(root->glob->paramlist) - 1;	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = i;	retval->paramtype = agg->aggtype;	retval->paramtypmod = -1;	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. */static Param *generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod){	Param	   *retval;	PlannerParamItem *pitem;	retval = makeNode(Param);	retval->paramkind = PARAM_EXEC;	retval->paramid = list_length(root->glob->paramlist);	retval->paramtype = paramtype;	retval->paramtypmod = paramtypmod;	pitem = makeNode(PlannerParamItem);	pitem->item = (Node *) retval;	pitem->abslevel = root->query_level;	root->glob->paramlist = lappend(root->glob->paramlist, pitem);	return retval;}/* * Get the datatype of the first column of the plan's output. * * This is stored for ARRAY_SUBLINK and for exprType(), which doesn't have any * way to get at the plan associated with a SubPlan node.  We really only need * the value for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency * we set it always. */static Oidget_first_col_type(Plan *plan){	TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist);	Assert(IsA(tent, TargetEntry));	Assert(!tent->resjunk);	return exprType((Node *) tent->expr);}/* * Convert a SubLink (as created by the parser) into a SubPlan. * * We are given the original SubLink and the already-processed testexpr * (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 a row comparison expression * tree containing InitPlan Param nodes. */static Node *make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual){	Query	   *subquery = (Query *) (slink->subselect);	double		tuple_fraction;	SubPlan    *splan;	Plan	   *plan;	PlannerInfo *subroot;	bool		isInitPlan;	Bitmapset  *tmpset;	int			paramid;	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 ROWCOMPARE subplans, use default behavior	 * (we're only expecting one row out, anyway).	 *	 * NOTE: if you change these numbers, also change cost_qual_eval_walker()	 * and get_initplan_cost() 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.	 */	plan = subquery_planner(root->glob, subquery,							root->query_level + 1,							tuple_fraction,							&subroot);	/*	 * Initialize the SubPlan node.  Note plan_id isn't set yet.	 */	splan = makeNode(SubPlan);	splan->subLinkType = slink->subLinkType;	splan->testexpr = NULL;	splan->paramIds = NIL;	splan->firstColType = get_first_col_type(plan);	splan->useHashTable = false;	/* At top level of a qual, can treat UNKNOWN the same as FALSE */	splan->unknownEqFalse = isTopQual;	splan->setParam = NIL;	splan->parParam = NIL;	splan->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 = list_nth(root->glob->paramlist, paramid);		if (pitem->abslevel == root->query_level)			splan->parParam = lappend_int(splan->parParam, paramid);	}	bms_free(tmpset);	/*	 * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or	 * ROWCOMPARE 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 ROWCOMPARE, we must modify the testexpr tree to contain	 * PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the	 * parser.	 */	if (splan->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)	{		Param	   *prm;		prm = generate_new_param(root, BOOLOID, -1);		splan->setParam = list_make1_int(prm->paramid);		isInitPlan = true;		result = (Node *) prm;	}	else if (splan->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)	{		TargetEntry *te = linitial(plan->targetlist);		Param	   *prm;		Assert(!te->resjunk);		prm = generate_new_param(root,								 exprType((Node *) te->expr),								 exprTypmod((Node *) te->expr));		splan->setParam = list_make1_int(prm->paramid);		isInitPlan = true;		result = (Node *) prm;	}	else if (splan->parParam == NIL && slink->subLinkType == ARRAY_SUBLINK)	{		TargetEntry *te = linitial(plan->targetlist);		Oid			arraytype;		Param	   *prm;		Assert(!te->resjunk);		arraytype = get_array_type(exprType((Node *) te->expr));		if (!OidIsValid(arraytype))			elog(ERROR, "could not find array type for datatype %s",				 format_type_be(exprType((Node *) te->expr)));		prm = generate_new_param(root,								 arraytype,								 exprTypmod((Node *) te->expr));		splan->setParam = list_make1_int(prm->paramid);		isInitPlan = true;		result = (Node *) prm;	}	else if (splan->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)	{		/* Adjust the Params */		List	   *params;		params = generate_subquery_params(root,										  plan->targetlist,										  &splan->paramIds);		result = convert_testexpr(root,								  testexpr,								  params);		splan->setParam = list_copy(splan->paramIds);		isInitPlan = true;		/*		 * The executable expression is returned to become part of the outer		 * plan's expression tree; it is not kept in the initplan node.		 */	}	else	{		List	   *args;		ListCell   *l;		if (testexpr)		{			List	   *params;			/* Adjust the Params in the testexpr */			params = generate_subquery_params(root,											  plan->targetlist,											  &splan->paramIds);			splan->testexpr = convert_testexpr(root,											   testexpr,											   params);		}		/*		 * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to		 * initPlans, even when they are uncorrelated or undirect correlated,		 * because we need to scan the output of the subplan for each outer		 * tuple.  But if it's an IN (= ANY) test, we might be able to use a		 * hashtable to avoid comparing all the tuples.		 */		if (subplan_is_hashable(slink, splan, plan))			splan->useHashTable = true;		/*		 * Otherwise, we have the option to tack a MATERIAL node onto the top		 * of the subplan, to reduce the cost of reading it repeatedly.  This		 * is pointless for a direct-correlated subplan, since we'd have to		 * recompute its results each time anyway.	For uncorrelated/undirect		 * correlated subplans, we add MATERIAL unless the subplan's top plan		 * node would materialize its output anyway.		 */		else if (splan->parParam == NIL)		{			bool		use_material;			switch (nodeTag(plan))			{				case T_Material:				case T_FunctionScan:				case T_Sort:					use_material = false;					break;				default:					use_material = true;					break;			}			if (use_material)				plan = materialize_finished_plan(plan);		}		/*		 * Make splan->args from parParam.		 */		args = NIL;		foreach(l, splan->parParam)		{			PlannerParamItem *pitem = list_nth(root->glob->paramlist,											   lfirst_int(l));			/*			 * The Var or Aggref has already been adjusted to have the correct			 * varlevelsup or agglevelsup.	We probably don't even need to			 * copy it again, but be safe.			 */			args = lappend(args, copyObject(pitem->item));		}		splan->args = args;		result = (Node *) splan;		isInitPlan = false;	}	/*	 * Add the subplan and its rtable to the global lists.	 */	root->glob->subplans = lappend(root->glob->subplans,								   plan);	root->glob->subrtables = lappend(root->glob->subrtables,									 subroot->parse->rtable);	splan->plan_id = list_length(root->glob->subplans);	if (isInitPlan)		root->init_plans = lappend(root->init_plans, splan);	/*	 * A parameterless subplan (not initplan) should be prepared to handle	 * REWIND efficiently.	If it has direct parameters then there's no point	 * since it'll be reset on each scan anyway; and if it's an initplan then	 * there's no point since it won't get re-run without parameter changes	 * anyway.	The input of a hashed subplan doesn't need REWIND either.	 */	if (splan->parParam == NIL && !isInitPlan && !splan->useHashTable)		root->glob->rewindPlanIDs = bms_add_member(root->glob->rewindPlanIDs,

⌨️ 快捷键说明

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