diff options
Diffstat (limited to 'src/backend/optimizer/plan/planmain.c')
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 200 |
1 files changed, 110 insertions, 90 deletions
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index a414a910fef..cfa134a3889 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.51 2000/02/07 04:41:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.52 2000/02/15 20:49:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" @@ -27,10 +28,11 @@ #include "utils/lsyscache.h" -static Plan *subplanner(Query *root, List *flat_tlist, List *qual); +static Plan *subplanner(Query *root, List *flat_tlist, List *qual, + double tuple_fraction); -/* +/*-------------------- * query_planner * Routine to create a query plan. It does so by first creating a * subplan for the topmost level of attributes in the query. Then, @@ -41,25 +43,41 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual); * be placed where and any relation level qualifications to be * satisfied. * - * tlist is the target list of the query (do NOT use root->targetList!) - * qual is the qualification of the query (likewise!) + * tlist is the target list of the query (do NOT use root->targetList!) + * qual is the qualification of the query (likewise!) + * tuple_fraction is the fraction of tuples we expect will be retrieved + * + * Note: the Query node now also includes a query_pathkeys field, which + * is both an input and an output of query_planner(). The input value + * signals query_planner that the indicated sort order is wanted in the + * final output plan. The output value is the actual pathkeys of the + * selected path. This might not be the same as what the caller requested; + * the caller must do pathkeys_contained_in() to decide whether an + * explicit sort is still needed. (The main reason query_pathkeys is a + * Query field and not a passed parameter is that the low-level routines + * in indxpath.c need to see it.) The pathkeys value passed to query_planner + * has not yet been "canonicalized", since the necessary info does not get + * computed until subplanner() scans the qual clauses. We canonicalize it + * inside subplanner() as soon as that task is done. The output value + * will be in canonical form as well. * - * Note: the Query node now also includes a query_pathkeys field, which - * is both an input and an output of query_planner(). The input value - * signals query_planner that the indicated sort order is wanted in the - * final output plan. The output value is the actual pathkeys of the - * selected path. This might not be the same as what the caller requested; - * the caller must do pathkeys_contained_in() to decide whether an - * explicit sort is still needed. (The main reason query_pathkeys is a - * Query field and not a passed parameter is that the low-level routines - * in indxpath.c need to see it.) + * tuple_fraction is interpreted as follows: + * 0 (or less): expect all tuples to be retrieved (normal case) + * 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) + * Note that while this routine and its subroutines treat a negative + * tuple_fraction the same as 0, union_planner has a different interpretation. * - * Returns a query plan. + * Returns a query plan. + *-------------------- */ Plan * query_planner(Query *root, List *tlist, - List *qual) + List *qual, + double tuple_fraction) { List *constant_qual = NIL; List *var_only_tlist; @@ -149,7 +167,7 @@ query_planner(Query *root, /* * Choose the best access path and build a plan for it. */ - subplan = subplanner(root, var_only_tlist, qual); + subplan = subplanner(root, var_only_tlist, qual, tuple_fraction); /* * Build a result node to control the plan if we have constant quals. @@ -192,33 +210,50 @@ query_planner(Query *root, * Subplanner creates an entire plan consisting of joins and scans * for processing a single level of attributes. * - * flat_tlist is the flattened target list - * qual is the qualification to be satisfied + * flat_tlist is the flattened target list + * qual is the qualification to be satisfied + * tuple_fraction is the fraction of tuples we expect will be retrieved * - * Returns a subplan. + * See query_planner() comments about the interpretation of tuple_fraction. * + * Returns a subplan. */ static Plan * subplanner(Query *root, List *flat_tlist, - List *qual) + List *qual, + double tuple_fraction) { RelOptInfo *final_rel; - Cost cheapest_cost; - Path *sortedpath; + Path *cheapestpath; + Path sort_path; /* dummy for result of cost_sort */ + Path *presortedpath; /* * Initialize the targetlist and qualification, adding entries to * base_rel_list as relation references are found (e.g., in the - * qualification, the targetlist, etc.) + * qualification, the targetlist, etc.). Restrict and join clauses + * are added to appropriate lists belonging to the mentioned relations, + * and we also build lists of equijoined keys for pathkey construction. */ root->base_rel_list = NIL; root->join_rel_list = NIL; + root->equi_key_list = NIL; make_var_only_tlist(root, flat_tlist); add_restrict_and_join_to_rels(root, qual); add_missing_rels_to_query(root); + /* + * We should now have all the pathkey equivalence sets built, + * so it's now possible to convert the requested query_pathkeys + * to canonical form. + */ + root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys); + + /* + * Ready to do the primary planning. + */ final_rel = make_one_rel(root); if (! final_rel) @@ -258,96 +293,81 @@ subplanner(Query *root, foreach(pathnode, final_rel->pathlist) { if (xfunc_do_predmig((Path *) lfirst(pathnode))) - set_cheapest(final_rel, final_rel->pathlist); + set_cheapest(final_rel); } } #endif /* - * Determine the cheapest path and create a subplan to execute it. + * Now that we have an estimate of the final rel's size, we can convert + * a tuple_fraction specified as an absolute count (ie, a LIMIT option) + * into a fraction of the total tuples. + */ + if (tuple_fraction >= 1.0) + tuple_fraction /= final_rel->rows; + + /* + * Determine the cheapest path, independently of any ordering + * considerations. We do, however, take into account whether the + * whole plan is expected to be evaluated or not. + */ + if (tuple_fraction <= 0.0 || tuple_fraction >= 1.0) + cheapestpath = final_rel->cheapest_total_path; + else + cheapestpath = + get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist, + NIL, + tuple_fraction); + + Assert(cheapestpath != NULL); + + /* + * Select the best path and create a subplan to execute it. * * If no special sort order is wanted, or if the cheapest path is - * already appropriately ordered, just use the cheapest path. + * already appropriately ordered, we use the cheapest path found above. */ if (root->query_pathkeys == NIL || pathkeys_contained_in(root->query_pathkeys, - final_rel->cheapestpath->pathkeys)) + cheapestpath->pathkeys)) { - root->query_pathkeys = final_rel->cheapestpath->pathkeys; - return create_plan(root, final_rel->cheapestpath); + root->query_pathkeys = cheapestpath->pathkeys; + return create_plan(root, cheapestpath); } /* * Otherwise, look to see if we have an already-ordered path that is - * cheaper than doing an explicit sort on cheapestpath. + * cheaper than doing an explicit sort on the cheapest-total-cost path. */ - cheapest_cost = final_rel->cheapestpath->path_cost + - cost_sort(root->query_pathkeys, final_rel->rows, final_rel->width); - - sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, - root->query_pathkeys, - false); - if (sortedpath) + cheapestpath = final_rel->cheapest_total_path; + cost_sort(&sort_path, root->query_pathkeys, + final_rel->rows, final_rel->width); + sort_path.startup_cost += cheapestpath->total_cost; + sort_path.total_cost += cheapestpath->total_cost; + + presortedpath = + get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist, + root->query_pathkeys, + tuple_fraction); + if (presortedpath) { - if (sortedpath->path_cost <= cheapest_cost) + if (compare_fractional_path_costs(presortedpath, &sort_path, + tuple_fraction) <= 0) { /* Found a better presorted path, use it */ - root->query_pathkeys = sortedpath->pathkeys; - return create_plan(root, sortedpath); + root->query_pathkeys = presortedpath->pathkeys; + return create_plan(root, presortedpath); } /* otherwise, doing it the hard way is still cheaper */ } - else - { - /* - * If we found no usable presorted path at all, it is possible - * that the user asked for descending sort order. Check to see - * if we can satisfy the pathkeys by using a backwards indexscan. - * To do this, we commute all the operators in the pathkeys and - * then look for a matching path that is an IndexPath. - */ - List *commuted_pathkeys = copyObject(root->query_pathkeys); - - if (commute_pathkeys(commuted_pathkeys)) - { - /* pass 'true' to force only IndexPaths to be considered */ - sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, - commuted_pathkeys, - true); - if (sortedpath && sortedpath->path_cost <= cheapest_cost) - { - /* - * Kluge here: since IndexPath has no representation for - * backwards scan, we have to convert to Plan format and - * then poke the result. - */ - Plan *sortedplan = create_plan(root, sortedpath); - List *sortedpathkeys; - - Assert(IsA(sortedplan, IndexScan)); - ((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection; - /* - * Need to generate commuted keys representing the actual - * sort order. This should succeed, probably, but just in - * case it does not, use the original root->query_pathkeys - * as a conservative approximation. - */ - sortedpathkeys = copyObject(sortedpath->pathkeys); - if (commute_pathkeys(sortedpathkeys)) - root->query_pathkeys = sortedpathkeys; - - return sortedplan; - } - } - } /* - * Nothing for it but to sort the cheapestpath --- but we let the - * caller do that. union_planner has to be able to add a sort node + * Nothing for it but to sort the cheapest-total-cost path --- but we let + * the caller do that. union_planner has to be able to add a sort node * anyway, so no need for extra code here. (Furthermore, the given - * pathkeys might involve something we can't compute here, such as - * an aggregate function...) + * pathkeys might involve something we can't compute here, such as an + * aggregate function...) */ - root->query_pathkeys = final_rel->cheapestpath->pathkeys; - return create_plan(root, final_rel->cheapestpath); + root->query_pathkeys = cheapestpath->pathkeys; + return create_plan(root, cheapestpath); } |