📄 planner.c
字号:
if (use_hashed_grouping || !sorted_path) best_path = cheapest_path; else best_path = sorted_path; /* * Check to see if it's possible to optimize MIN/MAX aggregates. If * so, we will forget all the work we did so far to choose a "regular" * path ... but we had to do it anyway to be able to tell which way is * cheaper. */ result_plan = optimize_minmax_aggregates(root, tlist, best_path); if (result_plan != NULL) { /* * optimize_minmax_aggregates generated the full plan, with the * right tlist, and it has no sort order. */ current_pathkeys = NIL; } else { /* * Normal case --- create a plan according to query_planner's * results. */ result_plan = create_plan(root, best_path); current_pathkeys = best_path->pathkeys; /* * create_plan() returns a plan with just a "flat" tlist of * required Vars. Usually we need to insert the sub_tlist as the * tlist of the top plan node. However, we can skip that if we * determined that whatever query_planner chose to return will be * good enough. */ if (need_tlist_eval) { /* * If the top-level plan node is one that cannot do expression * evaluation, we must insert a Result node to project the * desired tlist. */ if (!is_projection_capable_plan(result_plan)) { result_plan = (Plan *) make_result(sub_tlist, NULL, result_plan); } else { /* * Otherwise, just replace the subplan's flat tlist with * the desired tlist. */ result_plan->targetlist = sub_tlist; } /* * Also, account for the cost of evaluation of the sub_tlist. * * Up to now, we have only been dealing with "flat" tlists, * containing just Vars. So their evaluation cost is zero * according to the model used by cost_qual_eval() (or if you * prefer, the cost is factored into cpu_tuple_cost). Thus we * can avoid accounting for tlist cost throughout * query_planner() and subroutines. But now we've inserted a * tlist that might contain actual operators, sub-selects, etc * --- so we'd better account for its cost. * * Below this point, any tlist eval cost for added-on nodes * should be accounted for as we create those nodes. * Presently, of the node types we can add on, only Agg and * Group project new tlists (the rest just copy their input * tuples) --- so make_agg() and make_group() are responsible * for computing the added cost. */ cost_qual_eval(&tlist_cost, sub_tlist); result_plan->startup_cost += tlist_cost.startup; result_plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple * result_plan->plan_rows; } else { /* * Since we're using query_planner's tlist and not the one * make_subplanTargetList calculated, we have to refigure any * grouping-column indexes make_subplanTargetList computed. */ locate_grouping_columns(root, tlist, result_plan->targetlist, groupColIdx); } /* * Insert AGG or GROUP node if needed, plus an explicit sort step * if necessary. * * HAVING clause, if any, becomes qual of the Agg or Group node. */ if (use_hashed_grouping) { /* Hashed aggregate plan --- no sort needed */ result_plan = (Plan *) make_agg(root, tlist, (List *) parse->havingQual, AGG_HASHED, numGroupCols, groupColIdx, numGroups, agg_counts.numAggs, result_plan); /* Hashed aggregation produces randomly-ordered results */ current_pathkeys = NIL; } else if (parse->hasAggs) { /* Plain aggregate plan --- sort if needed */ AggStrategy aggstrategy; if (parse->groupClause) { if (!pathkeys_contained_in(group_pathkeys, current_pathkeys)) { result_plan = (Plan *) make_sort_from_groupcols(root, parse->groupClause, groupColIdx, result_plan); current_pathkeys = group_pathkeys; } aggstrategy = AGG_SORTED; /* * The AGG node will not change the sort ordering of its * groups, so current_pathkeys describes the result too. */ } else { aggstrategy = AGG_PLAIN; /* Result will be only one row anyway; no sort order */ current_pathkeys = NIL; } result_plan = (Plan *) make_agg(root, tlist, (List *) parse->havingQual, aggstrategy, numGroupCols, groupColIdx, numGroups, agg_counts.numAggs, result_plan); } else if (parse->groupClause) { /* * GROUP BY without aggregation, so insert a group node (plus * the appropriate sort node, if necessary). * * Add an explicit sort if we couldn't make the path come out * the way the GROUP node needs it. */ if (!pathkeys_contained_in(group_pathkeys, current_pathkeys)) { result_plan = (Plan *) make_sort_from_groupcols(root, parse->groupClause, groupColIdx, result_plan); current_pathkeys = group_pathkeys; } result_plan = (Plan *) make_group(root, tlist, (List *) parse->havingQual, numGroupCols, groupColIdx, dNumGroups, result_plan); /* The Group node won't change sort ordering */ } else if (root->hasHavingQual) { /* * No aggregates, and no GROUP BY, but we have a HAVING qual. * This is a degenerate case in which we are supposed to emit * either 0 or 1 row depending on whether HAVING succeeds. * Furthermore, there cannot be any variables in either HAVING * or the targetlist, so we actually do not need the FROM * table at all! We can just throw away the plan-so-far and * generate a Result node. This is a sufficiently unusual * corner case that it's not worth contorting the structure of * this routine to avoid having to generate the plan in the * first place. */ result_plan = (Plan *) make_result(tlist, parse->havingQual, NULL); } } /* end of non-minmax-aggregate case */ } /* end of if (setOperations) */ /* * If we were not able to make the plan come out in the right order, add * an explicit sort step. */ if (parse->sortClause) { if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys)) { result_plan = (Plan *) make_sort_from_sortclauses(root, parse->sortClause, result_plan); current_pathkeys = sort_pathkeys; } } /* * If there is a DISTINCT clause, add the UNIQUE node. */ if (parse->distinctClause) { result_plan = (Plan *) make_unique(result_plan, parse->distinctClause); /* * If there was grouping or aggregation, leave plan_rows as-is (ie, * assume the result was already mostly unique). If not, use the * number of distinct-groups calculated by query_planner. */ if (!parse->groupClause && !root->hasHavingQual && !parse->hasAggs) result_plan->plan_rows = dNumGroups; } /* * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node. */ if (parse->limitCount || parse->limitOffset) { result_plan = (Plan *) make_limit(result_plan, parse->limitOffset, parse->limitCount, offset_est, count_est); } /* * Return the actual output ordering in query_pathkeys for possible use by * an outer query level. */ root->query_pathkeys = current_pathkeys; return result_plan;}/* * preprocess_limit - do pre-estimation for LIMIT and/or OFFSET clauses * * We try to estimate the values of the LIMIT/OFFSET clauses, and pass the * results back in *count_est and *offset_est. These variables are set to * 0 if the corresponding clause is not present, and -1 if it's present * but we couldn't estimate the value for it. (The "0" convention is OK * for OFFSET but a little bit bogus for LIMIT: effectively we estimate * LIMIT 0 as though it were LIMIT 1. But this is in line with the planner's * usual practice of never estimating less than one row.) These values will * be passed to make_limit, which see if you change this code. * * The return value is the suitably adjusted tuple_fraction to use for * planning the query. This adjustment is not overridable, since it reflects * plan actions that grouping_planner() will certainly take, not assumptions * about context. */static doublepreprocess_limit(PlannerInfo *root, double tuple_fraction, int *offset_est, int *count_est){ Query *parse = root->parse; Node *est; double limit_fraction; /* Should not be called unless LIMIT or OFFSET */ Assert(parse->limitCount || parse->limitOffset); /* * Try to obtain the clause values. We use estimate_expression_value * primarily because it can sometimes do something useful with Params. */ if (parse->limitCount) { est = estimate_expression_value(parse->limitCount); if (est && IsA(est, Const)) { if (((Const *) est)->constisnull) { /* NULL indicates LIMIT ALL, ie, no limit */ *count_est = 0; /* treat as not present */ } else { *count_est = DatumGetInt32(((Const *) est)->constvalue); if (*count_est <= 0) *count_est = 1; /* force to at least 1 */ } } else *count_est = -1; /* can't estimate */ } else *count_est = 0; /* not present */ if (parse->limitOffset) { est = estimate_expression_value(parse->limitOffset); if (est && IsA(est, Const)) { if (((Const *) est)->constisnull) { /* Treat NULL as no offset; the executor will too */ *offset_est = 0; /* treat as not present */ } else { *offset_est = DatumGetInt32(((Const *) est)->constvalue); if (*offset_est < 0) *offset_est = 0; /* less than 0 is same as 0 */ } } else *offset_est = -1; /* can't estimate */ } else *offset_est = 0; /* not present */ if (*count_est != 0) { /* * A LIMIT clause limits the absolute number of tuples returned. * However, if it's not a constant LIMIT then we have to guess; for * lack of a better idea, assume 10% of the plan's result is wanted. */ if (*count_est < 0 || *offset_est < 0) { /* LIMIT or OFFSET is an expression ... punt ... */ limit_fraction = 0.10; } else { /* LIMIT (plus OFFSET, if any) is max number of tuples needed */ limit_fraction = (double) *count_est + (double) *offset_est; } /* * If we have absolute limits from both caller and LIMIT, use the * smaller value; likewise if they are both fractional. If one is * fractional and the other absolute, we can't easily determine which * is smaller, but we use the heuristic that the absolute will usually * be smaller. */ if (tuple_fraction >= 1.0) { if (limit_fraction >= 1.0) { /* both absolute */ tuple_fraction = Min(tuple_fraction, limit_fraction); } else { /* caller absolute, limit fractional; use caller's value */ } } else if (tuple_fraction > 0.0) { if (limit_fraction >= 1.0) { /* caller fractional, limit absolute; use limit */ tuple_fraction = limit_fraction; } else { /* both fractional */ tuple_fraction = Min(tuple_fraction, limit_fraction); } } else { /* no info from caller, just use limit */ tuple_fraction = limit_fraction; } } else if (*offset_est != 0 && tuple_fraction > 0.0) { /* * We have an OFFSET but no LIMIT. This acts entirely differently * from the LIMIT case: here, we need to increase rather than decrease * the caller's tuple_fraction, because the OFFSET acts to cause more * tuples to be fetched instead of fewer. This only matters if we got * a tuple_fraction > 0, however. * * As above, use 10% if OFFSET is present but unestimatable. */ if (*offset_est < 0) limit_fraction = 0.10; else limit_fraction = (double) *offset_est; /* * If we have absolute counts from both caller and OFFSET, add them
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -