diff options
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 185 |
1 files changed, 156 insertions, 29 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 28483fd4734..cf400f8df1b 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.74 2000/01/27 18:11:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.75 2000/02/15 20:49:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,7 +61,7 @@ planner(Query *parse) transformKeySetQuery(parse); - result_plan = union_planner(parse); + result_plan = union_planner(parse, -1.0 /* default case */); Assert(PlannerQueryLevel == 1); if (PlannerPlanId > 0) @@ -76,23 +76,39 @@ planner(Query *parse) return result_plan; } -/* +/*-------------------- * union_planner + * Invokes the planner on union-type queries (both regular UNIONs and + * appends produced by inheritance), recursing if necessary to get them + * all, then processes normal plans. * - * Invokes the planner on union queries if there are any left, - * recursing if necessary to get them all, then processes normal plans. + * parse is the querytree produced by the parser & rewriter. + * tuple_fraction is the fraction of tuples we expect will be retrieved * - * Returns a query plan. + * tuple_fraction is interpreted as follows: + * < 0: determine fraction by inspection of query (normal case) + * 0: expect all tuples to be retrieved + * 0 < tuple_fraction < 1: expect the given fraction of tuples available + * from the plan to be retrieved + * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples + * expected to be retrieved (ie, a LIMIT specification) + * The normal case is to pass -1, but some callers pass values >= 0 to + * override this routine's determination of the appropriate fraction. * + * Returns a query plan. + *-------------------- */ Plan * -union_planner(Query *parse) +union_planner(Query *parse, + double tuple_fraction) { List *tlist = parse->targetList; List *rangetable = parse->rtable; Plan *result_plan = (Plan *) NULL; AttrNumber *groupColIdx = NULL; List *current_pathkeys = NIL; + List *group_pathkeys; + List *sort_pathkeys; Index rt_index; /* @@ -139,6 +155,12 @@ union_planner(Query *parse) * Actually, for a normal UNION we have done an explicit sort; ought * to change interface to plan_union_queries to pass that info back! */ + + /* Calculate pathkeys that represent grouping/ordering requirements */ + group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, + tlist); + sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, + tlist); } else if ((rt_index = first_inherit_rt_entry(rangetable)) != -1) { @@ -176,6 +198,12 @@ union_planner(Query *parse) * We leave current_pathkeys NIL indicating we do not know sort order * of the Append-ed results. */ + + /* Calculate pathkeys that represent grouping/ordering requirements */ + group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, + tlist); + sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, + tlist); } else { @@ -229,32 +257,131 @@ union_planner(Query *parse) */ sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx); + /* Calculate pathkeys that represent grouping/ordering requirements */ + group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, + tlist); + sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, + tlist); + /* * Figure out whether we need a sorted result from query_planner. * * If we have a GROUP BY clause, then we want a result sorted * properly for grouping. Otherwise, if there is an ORDER BY clause, - * we want to sort by the ORDER BY clause. + * we want to sort by the ORDER BY clause. (Note: if we have both, + * and ORDER BY is a superset of GROUP BY, it would be tempting to + * request sort by ORDER BY --- but that might just leave us failing + * to exploit an available sort order at all. Needs more thought...) */ if (parse->groupClause) + parse->query_pathkeys = group_pathkeys; + else if (parse->sortClause) + parse->query_pathkeys = sort_pathkeys; + else + parse->query_pathkeys = NIL; + + /* + * Figure out whether we expect to retrieve all the tuples that the + * plan can generate, or to stop early due to a LIMIT or other + * factors. If the caller passed a value >= 0, believe that value, + * else do our own examination of the query context. + */ + if (tuple_fraction < 0.0) { - parse->query_pathkeys = - make_pathkeys_for_sortclauses(parse->groupClause, tlist); + /* Initial assumption is we need all the tuples */ + tuple_fraction = 0.0; + /* + * Check for a LIMIT. + * + * For now, we deliberately ignore the OFFSET clause, so that + * queries with the same LIMIT and different OFFSETs will get + * the same queryplan and therefore generate consistent results + * (to the extent the planner can guarantee that, anyway). + * XXX Perhaps it would be better to use the OFFSET too, and tell + * users to specify ORDER BY if they want consistent results + * across different LIMIT queries. + */ + if (parse->limitCount != NULL) + { + if (IsA(parse->limitCount, Const)) + { + Const *ccount = (Const *) parse->limitCount; + tuple_fraction = (double) ((int) (ccount->constvalue)); + /* the constant can legally be either 0 ("ALL") or a + * positive integer; either is consistent with our + * conventions for tuple_fraction. + */ + } + else + { + /* It's a PARAM ... don't know exactly what the limit + * will be, but for lack of a better idea assume 10% + * of the plan's result is wanted. + */ + tuple_fraction = 0.10; + } + } + /* + * Check for a retrieve-into-portal, ie DECLARE CURSOR. + * + * We have no real idea how many tuples the user will ultimately + * FETCH from a cursor, but it seems a good bet that he doesn't + * want 'em all. Optimize for 10% retrieval (you gotta better + * number?) + */ + if (parse->isPortal) + tuple_fraction = 0.10; } - else if (parse->sortClause) + /* + * Adjust tuple_fraction if we see that we are going to apply + * grouping/aggregation/etc. This is not overridable by the + * caller, since it reflects plan actions that this routine + * will certainly take, not assumptions about context. + */ + if (parse->groupClause) { - parse->query_pathkeys = - make_pathkeys_for_sortclauses(parse->sortClause, tlist); + /* + * In GROUP BY mode, we have the little problem that we don't + * really know how many input tuples will be needed to make a + * group, so we can't translate an output LIMIT count into an + * input count. For lack of a better idea, assume 10% of the + * input data will be processed if there is any output limit. + */ + if (tuple_fraction > 0.0) + tuple_fraction = 0.10; + /* + * If both GROUP BY and ORDER BY are specified, we will need + * two levels of sort --- and, therefore, certainly need to + * read all the input tuples --- unless ORDER BY is a subset + * of GROUP BY. (Although we are comparing non-canonicalized + * pathkeys here, it should be OK since they will both contain + * only single-element sublists at this point. See pathkeys.c.) + */ + if (parse->groupClause && parse->sortClause && + ! pathkeys_contained_in(sort_pathkeys, group_pathkeys)) + tuple_fraction = 0.0; } - else + else if (parse->hasAggs) { - parse->query_pathkeys = NIL; + /* Ungrouped aggregate will certainly want all the input tuples. */ + tuple_fraction = 0.0; + } + else if (parse->distinctClause) + { + /* + * SELECT DISTINCT, like GROUP, will absorb an unpredictable + * number of input tuples per output tuple. So, fall back to + * our same old 10% default... + */ + if (tuple_fraction > 0.0) + tuple_fraction = 0.10; } /* Generate the (sub) plan */ result_plan = query_planner(parse, sub_tlist, - (List *) parse->qual); + (List *) parse->qual, + tuple_fraction); /* query_planner returns actual sort order (which is not * necessarily what we requested) in query_pathkeys. @@ -267,6 +394,13 @@ union_planner(Query *parse) elog(ERROR, "union_planner: failed to create plan"); /* + * We couldn't canonicalize group_pathkeys and sort_pathkeys before + * running query_planner(), so do it now. + */ + group_pathkeys = canonicalize_pathkeys(parse, group_pathkeys); + sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys); + + /* * If we have a GROUP BY clause, insert a group node (plus the * appropriate sort node, if necessary). */ @@ -274,7 +408,6 @@ union_planner(Query *parse) { bool tuplePerGroup; List *group_tlist; - List *group_pathkeys; bool is_sorted; /* @@ -300,8 +433,6 @@ union_planner(Query *parse) * Figure out whether the path result is already ordered the way we * need it --- if so, no need for an explicit sort step. */ - group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, - tlist); if (pathkeys_contained_in(group_pathkeys, current_pathkeys)) { is_sorted = true; /* no sort needed now */ @@ -352,15 +483,15 @@ union_planner(Query *parse) } /* - * If aggregate is present, insert the agg node + * If aggregate is present, insert the Agg node + * + * HAVING clause, if any, becomes qual of the Agg node */ if (parse->hasAggs) { - result_plan = (Plan *) make_agg(tlist, result_plan); - - /* HAVING clause, if any, becomes qual of the Agg node */ - result_plan->qual = (List *) parse->havingQual; - + result_plan = (Plan *) make_agg(tlist, + (List *) parse->havingQual, + result_plan); /* Note: Agg does not affect any existing sort order of the tuples */ } @@ -370,10 +501,6 @@ union_planner(Query *parse) */ if (parse->sortClause) { - List *sort_pathkeys; - - sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, - tlist); if (! pathkeys_contained_in(sort_pathkeys, current_pathkeys)) { result_plan = make_sortplan(tlist, parse->sortClause, result_plan); |