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 + -
显示快捷键?