subselect.c

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

C
1,118
字号
		result = (Node *) prm;	}	else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)	{		List	   *exprs;		/* Convert the lefthand exprs and oper OIDs into executable exprs */		exprs = convert_sublink_opers(lefthand,									  slink->operOids,									  plan->targetlist,									  0,									  &node->paramIds);		node->setParam = listCopy(node->paramIds);		PlannerInitPlan = lappend(PlannerInitPlan, node);		/*		 * The executable expressions are returned to become part of the		 * outer plan's expression tree; they are not kept in the initplan		 * node.		 */		if (length(exprs) > 1)			result = (Node *) (node->useOr ? make_orclause(exprs) :							   make_andclause(exprs));		else			result = (Node *) lfirst(exprs);	}	else	{		List	   *args;		/*		 * 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, node))			node->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 if		 * the subplan's top plan node is anything more complicated than a		 * plain sequential scan, and we do it even for seqscan if the		 * qual appears selective enough to eliminate many tuples.		 */		else if (node->parParam == NIL)		{			bool		use_material;			switch (nodeTag(plan))			{				case T_SeqScan:					if (plan->initPlan)						use_material = true;					else					{						Selectivity qualsel;						qualsel = clauselist_selectivity(subquery,														 plan->qual,														 0, JOIN_INNER);						/* Is 10% selectivity a good threshold?? */						use_material = qualsel < 0.10;					}					break;				case T_Material:				case T_FunctionScan:				case T_Sort:					/*					 * Don't add another Material node if there's one					 * already, nor if the top node is any other type that					 * materializes its output anyway.					 */					use_material = false;					break;				default:					use_material = true;					break;			}			if (use_material)				node->plan = plan = materialize_finished_plan(plan);		}		/* Convert the lefthand exprs and oper OIDs into executable exprs */		node->exprs = convert_sublink_opers(lefthand,											slink->operOids,											plan->targetlist,											0,											&node->paramIds);		/*		 * Make node->args from parParam.		 */		args = NIL;		foreach(lst, node->parParam)		{			PlannerParamItem *pitem = nth(lfirsti(lst), PlannerParamList);			/*			 * 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));		}		node->args = args;		result = (Node *) node;	}	return result;}/* * convert_sublink_opers: given a lefthand-expressions list and a list of * operator OIDs, build a list of actually executable expressions.	The * righthand sides of the expressions are Params or Vars representing the * results of the sub-select. * * If rtindex is 0, we build Params to represent the sub-select outputs. * The paramids of the Params created are returned in the *righthandIds list. * * If rtindex is not 0, we build Vars using that rtindex as varno.	Copies * of the Var nodes are returned in *righthandIds (this is a bit of a type * cheat, but we can get away with it). */static List *convert_sublink_opers(List *lefthand, List *operOids,					  List *targetlist, int rtindex,					  List **righthandIds){	List	   *result = NIL;	List	   *lst;	*righthandIds = NIL;	foreach(lst, operOids)	{		Oid			opid = lfirsto(lst);		Node	   *leftop = lfirst(lefthand);		TargetEntry *te = lfirst(targetlist);		Node	   *rightop;		Operator	tup;		Assert(!te->resdom->resjunk);		if (rtindex)		{			/* Make the Var node representing the subplan's result */			rightop = (Node *) makeVar(rtindex,									   te->resdom->resno,									   te->resdom->restype,									   te->resdom->restypmod,									   0);			/*			 * Copy it for caller.  NB: we need a copy to avoid having			 * doubly-linked substructure in the modified parse tree.			 */			*righthandIds = lappend(*righthandIds, copyObject(rightop));		}		else		{			/* Make the Param node representing the subplan's result */			Param	   *prm;			prm = generate_new_param(te->resdom->restype,									 te->resdom->restypmod);			/* Record its ID */			*righthandIds = lappendi(*righthandIds, prm->paramid);			rightop = (Node *) prm;		}		/* Look up the operator to pass to make_op_expr */		tup = SearchSysCache(OPEROID,							 ObjectIdGetDatum(opid),							 0, 0, 0);		if (!HeapTupleIsValid(tup))			elog(ERROR, "cache lookup failed for operator %u", opid);		/*		 * Make the expression node.		 *		 * Note: we use make_op_expr in case runtime type conversion function		 * calls must be inserted for this operator!  (But we are not		 * expecting to have to resolve unknown Params, so it's okay to		 * pass a null pstate.)		 */		result = lappend(result,						 make_op_expr(NULL,									  tup,									  leftop,									  rightop,									  exprType(leftop),									  te->resdom->restype));		ReleaseSysCache(tup);		lefthand = lnext(lefthand);		targetlist = lnext(targetlist);	}	return result;}/* * subplan_is_hashable: decide whether we can implement a subplan by hashing * * Caution: the SubPlan node is not completely filled in yet.  We can rely * on its plan and parParam fields, however. */static boolsubplan_is_hashable(SubLink *slink, SubPlan *node){	double		subquery_size;	List	   *opids;	/*	 * The sublink type must be "= ANY" --- that is, an IN operator. (We	 * require the operator name to be unqualified, which may be overly	 * paranoid, or may not be.)  XXX since we also check that the	 * operators are hashable, the test on operator name may be redundant?	 */	if (slink->subLinkType != ANY_SUBLINK)		return false;	if (length(slink->operName) != 1 ||		strcmp(strVal(lfirst(slink->operName)), "=") != 0)		return false;	/*	 * The subplan must not have any direct correlation vars --- else we'd	 * have to recompute its output each time, so that the hashtable	 * wouldn't gain anything.	 */	if (node->parParam != NIL)		return false;	/*	 * The estimated size of the subquery result must fit in SortMem. (XXX	 * what about hashtable overhead?)	 */	subquery_size = node->plan->plan_rows *		(MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleData)));	if (subquery_size > SortMem * 1024L)		return false;	/*	 * The combining operators must be hashable, strict, and	 * self-commutative. The need for hashability is obvious, since we	 * want to use hashing. Without strictness, behavior in the presence	 * of nulls is too unpredictable.  (We actually must assume even more	 * than plain strictness, see nodeSubplan.c for details.)  And	 * commutativity ensures that the left and right datatypes are the	 * same; this allows us to assume that the combining operators are	 * equality for the righthand datatype, so that they can be used to	 * compare righthand tuples as well as comparing lefthand to righthand	 * tuples.	(This last restriction could be relaxed by using two	 * different sets of operators with the hash table, but there is no	 * obvious usefulness to that at present.)	 */	foreach(opids, slink->operOids)	{		Oid			opid = lfirsto(opids);		HeapTuple	tup;		Form_pg_operator optup;		tup = SearchSysCache(OPEROID,							 ObjectIdGetDatum(opid),							 0, 0, 0);		if (!HeapTupleIsValid(tup))			elog(ERROR, "cache lookup failed for operator %u", opid);		optup = (Form_pg_operator) GETSTRUCT(tup);		if (!optup->oprcanhash || optup->oprcom != opid ||			!func_strict(optup->oprcode))		{			ReleaseSysCache(tup);			return false;		}		ReleaseSysCache(tup);	}	return true;}/* * convert_IN_to_join: can we convert an IN SubLink to join style? * * The caller has found a SubLink at the top level of WHERE, but has not * checked the properties of the SubLink at all.  Decide whether it is * appropriate to process this SubLink in join style.  If not, return NULL. * If so, build the qual clause(s) to replace the SubLink, and return them. * * Side effects of a successful conversion include adding the SubLink's * subselect to the query's rangetable and adding an InClauseInfo node to * its in_info_list. */Node *convert_IN_to_join(Query *parse, SubLink *sublink){	Query	   *subselect = (Query *) sublink->subselect;	Relids		left_varnos;	int			rtindex;	RangeTblEntry *rte;	RangeTblRef *rtr;	InClauseInfo *ininfo;	List	   *exprs;	/*	 * The sublink type must be "= ANY" --- that is, an IN operator. (We	 * require the operator name to be unqualified, which may be overly	 * paranoid, or may not be.)	 */	if (sublink->subLinkType != ANY_SUBLINK)		return NULL;	if (length(sublink->operName) != 1 ||		strcmp(strVal(lfirst(sublink->operName)), "=") != 0)		return NULL;	/*	 * The sub-select must not refer to any Vars of the parent query.	 * (Vars of higher levels should be okay, though.)	 */	if (contain_vars_of_level((Node *) subselect, 1))		return NULL;	/*	 * The left-hand expressions must contain some Vars of the current	 * query, else it's not gonna be a join.	 */	left_varnos = pull_varnos((Node *) sublink->lefthand);	if (bms_is_empty(left_varnos))		return NULL;	/*	 * The left-hand expressions mustn't be volatile.  (Perhaps we should	 * test the combining operators, too?  We'd only need to point the	 * function directly at the sublink ...)	 */	if (contain_volatile_functions((Node *) sublink->lefthand))		return NULL;	/*	 * Okay, pull up the sub-select into top range table and jointree.	 *	 * We rely here on the assumption that the outer query has no references	 * to the inner (necessarily true, other than the Vars that we build	 * below).	Therefore this is a lot easier than what	 * pull_up_subqueries has to go through.	 */	rte = addRangeTableEntryForSubquery(NULL,										subselect,										makeAlias("IN_subquery", NIL),										false);	parse->rtable = lappend(parse->rtable, rte);	rtindex = length(parse->rtable);	rtr = makeNode(RangeTblRef);	rtr->rtindex = rtindex;	parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr);	/*	 * Now build the InClauseInfo node.	 */	ininfo = makeNode(InClauseInfo);	ininfo->lefthand = left_varnos;	ininfo->righthand = bms_make_singleton(rtindex);	parse->in_info_list = lcons(ininfo, parse->in_info_list);	/*	 * Build the result qual expressions.  As a side effect,

⌨️ 快捷键说明

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