subselect.c

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

C
1,470
字号
												   splan->plan_id);	return result;}/* * generate_subquery_params: build a list of Params representing the output * columns of a sublink's sub-select, given the sub-select's targetlist. * * We also return an integer list of the paramids of the Params. */static List *generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds){	List	   *result;	List	   *ids;	ListCell   *lc;	result = ids = NIL;	foreach(lc, tlist)	{		TargetEntry *tent = (TargetEntry *) lfirst(lc);		Param	   *param;		if (tent->resjunk)			continue;		param = generate_new_param(root,								   exprType((Node *) tent->expr),								   exprTypmod((Node *) tent->expr));		result = lappend(result, param);		ids = lappend_int(ids, param->paramid);	}	*paramIds = ids;	return result;}/* * generate_subquery_vars: build a list of Vars representing the output * columns of a sublink's sub-select, given the sub-select's targetlist. * The Vars have the specified varno (RTE index). */static List *generate_subquery_vars(PlannerInfo *root, List *tlist, Index varno){	List	   *result;	ListCell   *lc;	result = NIL;	foreach(lc, tlist)	{		TargetEntry *tent = (TargetEntry *) lfirst(lc);		Var		   *var;		if (tent->resjunk)			continue;		var = makeVar(varno,					  tent->resno,					  exprType((Node *) tent->expr),					  exprTypmod((Node *) tent->expr),					  0);		result = lappend(result, var);	}	return result;}/* * convert_testexpr: convert the testexpr given by the parser into * actually executable form.  This entails replacing PARAM_SUBLINK Params * with Params or Vars representing the results of the sub-select.  The * nodes to be substituted are passed in as the List result from * generate_subquery_params or generate_subquery_vars. * * The given testexpr has already been recursively processed by * process_sublinks_mutator.  Hence it can no longer contain any * PARAM_SUBLINK Params for lower SubLink nodes; we can safely assume that * any we find are for our own level of SubLink. */static Node *convert_testexpr(PlannerInfo *root,				 Node *testexpr,				 List *subst_nodes){	convert_testexpr_context context;	context.root = root;	context.subst_nodes = subst_nodes;	return convert_testexpr_mutator(testexpr, &context);}static Node *convert_testexpr_mutator(Node *node,						 convert_testexpr_context *context){	if (node == NULL)		return NULL;	if (IsA(node, Param))	{		Param	   *param = (Param *) node;		if (param->paramkind == PARAM_SUBLINK)		{			if (param->paramid <= 0 ||				param->paramid > list_length(context->subst_nodes))				elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);			/*			 * We copy the list item to avoid having doubly-linked			 * substructure in the modified parse tree.  This is probably			 * unnecessary when it's a Param, but be safe.			 */			return (Node *) copyObject(list_nth(context->subst_nodes,												param->paramid - 1));		}	}	return expression_tree_mutator(node,								   convert_testexpr_mutator,								   (void *) context);}/* * 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, Plan *plan){	double		subquery_size;	ListCell   *l;	/*	 * The sublink type must be "= ANY" --- that is, an IN operator.  We	 * expect that the test expression will be either a single OpExpr, or an	 * AND-clause containing OpExprs.  (If it's anything else then the parser	 * must have determined that the operators have non-equality-like	 * semantics.  In the OpExpr case we can't be sure what the operator's	 * semantics are like, but the test below for hashability will reject	 * anything that's not equality.)	 */	if (slink->subLinkType != ANY_SUBLINK)		return false;	if (slink->testexpr == NULL ||		(!IsA(slink->testexpr, OpExpr) &&		 !and_clause(slink->testexpr)))		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 work_mem. (Note:	 * we use sizeof(HeapTupleHeaderData) here even though the tuples will	 * actually be stored as MinimalTuples; this provides some fudge factor	 * for hashtable overhead.)	 */	subquery_size = plan->plan_rows *		(MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));	if (subquery_size > work_mem * 1024L)		return false;	/*	 * The combining operators must be hashable and strict. 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: they can't yield	 * NULL for non-null inputs, either (see nodeSubplan.c).  However, hash	 * indexes and hash joins assume that too.	 */	if (IsA(slink->testexpr, OpExpr))	{		if (!hash_ok_operator((OpExpr *) slink->testexpr))			return false;	}	else	{		foreach(l, ((BoolExpr *) slink->testexpr)->args)		{			Node	   *andarg = (Node *) lfirst(l);			if (!IsA(andarg, OpExpr))				return false;	/* probably can't happen */			if (!hash_ok_operator((OpExpr *) andarg))				return false;		}	}	return true;}static boolhash_ok_operator(OpExpr *expr){	Oid			opid = expr->opno;	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 || !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(PlannerInfo *root, SubLink *sublink){	Query	   *parse = root->parse;	Query	   *subselect = (Query *) sublink->subselect;	List	   *in_operators;	List	   *left_exprs;	List	   *right_exprs;	Relids		left_varnos;	int			rtindex;	RangeTblEntry *rte;	RangeTblRef *rtr;	List	   *subquery_vars;	InClauseInfo *ininfo;	Node	   *result;	/*	 * The sublink type must be "= ANY" --- that is, an IN operator.  We	 * expect that the test expression will be either a single OpExpr, or an	 * AND-clause containing OpExprs.  (If it's anything else then the parser	 * must have determined that the operators have non-equality-like	 * semantics.  In the OpExpr case we can't be sure what the operator's	 * semantics are like, and must check for ourselves.)	 */	if (sublink->subLinkType != ANY_SUBLINK)		return NULL;	if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))	{		OpExpr	   *op = (OpExpr *) sublink->testexpr;		Oid			opno = op->opno;		List	   *opfamilies;		List	   *opstrats;		if (list_length(op->args) != 2)			return NULL;				/* not binary operator? */		get_op_btree_interpretation(opno, &opfamilies, &opstrats);		if (!list_member_int(opstrats, ROWCOMPARE_EQ))			return NULL;		in_operators = list_make1_oid(opno);		left_exprs = list_make1(linitial(op->args));		right_exprs = list_make1(lsecond(op->args));	}	else if (and_clause(sublink->testexpr))	{		ListCell   *lc;		/* OK, but we need to extract the per-column info */		in_operators = left_exprs = right_exprs = NIL;		foreach(lc, ((BoolExpr *) sublink->testexpr)->args)		{			OpExpr	   *op = (OpExpr *) lfirst(lc);			if (!IsA(op, OpExpr))		/* probably shouldn't happen */				return NULL;			if (list_length(op->args) != 2)				return NULL;			/* not binary operator? */			in_operators = lappend_oid(in_operators, op->opno);			left_exprs = lappend(left_exprs, linitial(op->args));			right_exprs = lappend(right_exprs, lsecond(op->args));		}	}	else		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 *) left_exprs);	if (bms_is_empty(left_varnos))		return NULL;	/* ... and the right-hand expressions better not contain Vars at all */	Assert(!contain_var_clause((Node *) right_exprs));	/*	 * The combining operators and left-hand expressions mustn't be volatile.	 */	if (contain_volatile_functions(sublink->testexpr))		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 = list_length(parse->rtable);	rtr = makeNode(RangeTblRef);	rtr->rtindex = rtindex;	parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr);	/*	 * Build a list of Vars representing the subselect outputs.	 */	subquery_vars = generate_subquery_vars(root,										   subselect->targetList,										   rtindex);	/*	 * Build the result qual expression, replacing Params with these Vars.	 */	result = convert_testexpr(root,							  sublink->testexpr,							  subquery_vars);	/*	 * Now build the InClauseInfo node.	 */	ininfo = makeNode(InClauseInfo);	ininfo->lefthand = left_varnos;	ininfo->righthand = bms_make_singleton(rtindex);	ininfo->in_operators = in_operators;	/*	 * ininfo->sub_targetlist must be filled with a list of expressions that	 * would need to be unique-ified if we try to implement the IN using a	 * regular join to unique-ified subquery output.  This is most easily done	 * by applying convert_testexpr to just the RHS inputs of the testexpr	 * operators.  That handles cases like type coercions of the subquery	 * outputs, clauses dropped due to const-simplification, etc.	 */	ininfo->sub_targetlist = (List *) convert_testexpr(root,													   (Node *) right_exprs,													   subquery_vars);	/* Add the completed node to the query's list */	root->in_info_list = lappend(root->in_info_list, ininfo);	return result;}/* * Replace correlation vars (uplevel vars) with Params. * * Uplevel aggregates are replaced, too. * * Note: it is critical that this runs immediately after SS_process_sublinks. * Since we do not recurse into the arguments of uplevel aggregates, they will * get copied to the appropriate subplan args list in the parent query with * uplevel vars not replaced by Params, but only adjusted in level (see * replace_outer_agg).	That's exactly what we want for the vars of the parent * level --- but if an aggregate's argument contains any further-up variables, * they have to be replaced with Params in their turn.	That will happen when * the parent level runs SS_replace_correlation_vars.  Therefore it must do * so after expanding its sublinks to subplans.  And we don't want any steps * in between, else those steps would never get applied to the aggregate * argument expressions, either in the parent or the child level. */Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr){	/* No setup needed for tree walk, so away we go */	return replace_correlation_vars_mutator(expr, root);}static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root){	if (node == NULL)		return NULL;	if (IsA(node, Var))	{		if (((Var *) node)->varlevelsup > 0)			return (Node *) replace_outer_var(root, (Var *) node);	}	if (IsA(node, Aggref))	{		if (((Aggref *) node)->agglevelsup > 0)			return (Node *) replace_outer_agg(root, (Aggref *) node);	}	return expression_tree_mutator(node,								   replace_correlation_vars_mutator,								   (void *) root);}/* * Expand SubLinks to SubPlans in the given expression. * * The isQual argument tells whether or not this expression is a WHERE/HAVING * qualifier expression.  If it is, any sublinks appearing at top level need * not distinguish FALSE from UNKNOWN return values. */Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual){	process_sublinks_context context;	context.root = root;	context.isTopQual = isQual;	return process_sublinks_mutator(expr, &context);}static Node *process_sublinks_mutator(Node *node, process_sublinks_context *context){	process_sublinks_context locContext;	locContext.root = context->root;	if (node == NULL)		return NULL;	if (IsA(node, SubLink))	{		SubLink    *sublink = (SubLink *) node;		Node	   *testexpr;		/*		 * First, recursively process the lefthand-side expressions, if any.		 * They're not top-level anymore.		 */		locContext.isTopQual = false;		testexpr = process_sublinks_mutator(sublink->testexpr, &locContext);		/*		 * Now build the SubPlan node and make the expr to return.		 */		return make_subplan(context->root,							sublink,							testexpr,							context->isTopQual);	}	/*	 * We should never see a SubPlan expression in the input (since this is	 * the very routine that creates 'em to begin with).  We shouldn't find	 * ourselves invoked directly on a Query, either.	 */	Assert(!is_subplan(node));	Assert(!IsA(node, Query));	/*	 * Because make_subplan() could return an AND or OR clause, we have to	 * take steps to preserve AND/OR flatness of a qual.  We assume the input	 * has been AND/OR flattened and so we need no recursion here.	 *	 * If we recurse down through anything other than an AND node, we are	 * definitely not at top qual level anymore.  (Due to the coding here, we	 * will not get called on the List subnodes of an AND, so no check is	 * needed for List.)	 */	if (and_clause(node))	{

⌨️ 快捷键说明

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