📄 subselect.c
字号:
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(l, node->parParam) { PlannerParamItem *pitem = list_nth(PlannerParamList, 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)); } 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; ListCell *l, *lefthand_item, *tlist_item; *righthandIds = NIL; lefthand_item = list_head(lefthand); tlist_item = list_head(targetlist); foreach(l, operOids) { Oid opid = lfirst_oid(l); Node *leftop = (Node *) lfirst(lefthand_item); TargetEntry *te = (TargetEntry *) lfirst(tlist_item); Node *rightop; Operator tup; Assert(!te->resjunk); if (rtindex) { /* Make the Var node representing the subplan's result */ rightop = (Node *) makeVar(rtindex, te->resno, exprType((Node *) te->expr), exprTypmod((Node *) te->expr), 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(exprType((Node *) te->expr), exprTypmod((Node *) te->expr)); /* Record its ID */ *righthandIds = lappend_int(*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), exprType((Node *) te->expr))); ReleaseSysCache(tup); lefthand_item = lnext(lefthand_item); tlist_item = lnext(tlist_item); } 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; ListCell *l; /* * 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 (list_length(slink->operName) != 1 || strcmp(strVal(linitial(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 work_mem. (XXX * what about hashtable overhead?) */ subquery_size = node->plan->plan_rows * (MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleData))); if (subquery_size > work_mem * 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(l, slink->operOids) { Oid opid = lfirst_oid(l); 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(PlannerInfo *root, SubLink *sublink){ Query *parse = root->parse; 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 (list_length(sublink->operName) != 1 || strcmp(strVal(linitial(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 = list_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); root->in_info_list = lappend(root->in_info_list, ininfo); /* * Build the result qual expressions. As a side effect, * ininfo->sub_targetlist is filled with a list of Vars representing the * subselect outputs. */ exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, rtindex, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs);}/* * 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(Node *expr){ /* No setup needed for tree walk, so away we go */ return replace_correlation_vars_mutator(expr, NULL);}static Node *replace_correlation_vars_mutator(Node *node, void *context){ if (node == NULL) return NULL; if (IsA(node, Var)) { if (((Var *) node)->varlevelsup > 0) return (Node *) replace_outer_var((Var *) node); } if (IsA(node, Aggref)) { if (((Aggref *) node)->agglevelsup > 0) return (Node *) replace_outer_agg((Aggref *) node); } return expression_tree_mutator(node, replace_correlation_vars_mutator, context);}/* * 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(Node *expr, bool isQual){ /* The only context needed is the initial are-we-in-a-qual flag */ return process_sublinks_mutator(expr, &isQual);}static Node *process_sublinks_mutator(Node *node, bool *isTopQual){ bool locTopQual; if (node == NULL) return NULL; if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; List *lefthand; /* * First, recursively process the lefthand-side expressions, if any. */ locTopQual = false; lefthand = (List *) process_sublinks_mutator((Node *) sublink->lefthand, &locTopQual); /* * Now build the SubPlan node and make the expr to return. */ return make_subplan(sublink, lefthand, *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)) { List *newargs = NIL; ListCell *l; /* Still at qual top-level */ locTopQual = *isTopQual; foreach(l, ((BoolExpr *) node)->args) { Node *newarg; newarg = process_sublinks_mutator(lfirst(l), (void *) &locTopQual); if (and_clause(newarg)) newargs = list_concat(newargs, ((BoolExpr *) newarg)->args); else newargs = lappend(newargs, newarg); } return (Node *) make_andclause(newargs); } /* otherwise not at qual top-level */ locTopQual = false; if (or_clause(node)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -