diff options
Diffstat (limited to 'src/backend/optimizer/plan/planmain.c')
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 264 |
1 files changed, 67 insertions, 197 deletions
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 41f914b8f91..4354a5eb035 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,15 +14,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.70 2002/09/02 02:47:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.71 2002/11/06 00:00:44 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" - #include "optimizer/clauses.h" -#include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" @@ -31,31 +29,37 @@ #include "utils/memutils.h" -static Plan *subplanner(Query *root, List *flat_tlist, - double tuple_fraction); - - /*-------------------- * query_planner - * Generate a plan for a basic query, which may involve joins but - * not any fancier features. + * Generate a path (that is, a simplified plan) for a basic query, + * which may involve joins but not any fancier features. * + * Since query_planner does not handle the toplevel processing (grouping, + * sorting, etc) it cannot select the best path by itself. It selects + * two paths: the cheapest path that produces the required tuples, independent + * of any ordering considerations, and the cheapest path that produces the + * required tuples in the required ordering, if there is a path that + * can produce them without an explicit top-level sort step. The caller + * (grouping_planner) will make the final decision about which to use. + * + * Input parameters: + * root is the query to plan * tlist is the target list the query should produce (NOT root->targetList!) * 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. + * Output parameters: + * *cheapest_path receives the overall-cheapest path for the query + * *sorted_path receives the cheapest presorted path for the query, + * if any (it may be NULL, or the same as cheapest_path) + * + * Note: the Query node 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. But this value has not yet been "canonicalized", since the needed + * info does not get computed until we scan the qual clauses. We canonicalize + * it as soon as that task is done. (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) @@ -66,18 +70,14 @@ static Plan *subplanner(Query *root, List *flat_tlist, * Note that while this routine and its subroutines treat a negative * tuple_fraction the same as 0, grouping_planner has a different * interpretation. - * - * Returns a query plan. *-------------------- */ -Plan * -query_planner(Query *root, - List *tlist, - double tuple_fraction) +void +query_planner(Query *root, List *tlist, double tuple_fraction, + Path **cheapest_path, Path **sorted_path) { List *constant_quals; - List *var_only_tlist; - Plan *subplan; + RelOptInfo *final_rel; /* * If the query has an empty join tree, then it's something easy like @@ -85,11 +85,10 @@ query_planner(Query *root, */ if (root->jointree->fromlist == NIL) { - root->query_pathkeys = NIL; /* signal unordered result */ - - /* Make childless Result node to evaluate given tlist. */ - return (Plan *) make_result(tlist, root->jointree->quals, - (Plan *) NULL); + *cheapest_path = (Path *) create_result_path(NULL, NULL, + (List *) root->jointree->quals); + *sorted_path = NULL; + return; } /* @@ -107,80 +106,8 @@ query_planner(Query *root, &constant_quals); /* - * Create a target list that consists solely of (resdom var) target - * list entries, i.e., contains no arbitrary expressions. - * - * All subplan nodes will have "flat" (var-only) tlists. - * - * This implies that all expression evaluations are done at the root of - * the plan tree. Once upon a time there was code to try to push - * expensive function calls down to lower plan nodes, but that's dead - * code and has been for a long time... - */ - var_only_tlist = flatten_tlist(tlist); - - /* - * Choose the best access path and build a plan for it. + * init planner lists to empty */ - subplan = subplanner(root, var_only_tlist, tuple_fraction); - - /* - * Build a result node to control the plan if we have constant quals, - * or if the top-level plan node is one that cannot do expression - * evaluation (it won't be able to evaluate the requested tlist). - * Currently, the only plan node we might see here that falls into - * that category is Append. - * - * XXX future improvement: if the given tlist is flat anyway, we don't - * really need a Result node. - */ - if (constant_quals || IsA(subplan, Append)) - { - /* - * The result node will also be responsible for evaluating the - * originally requested tlist. - */ - subplan = (Plan *) make_result(tlist, - (Node *) constant_quals, - subplan); - } - else - { - /* - * Replace the toplevel plan node's flattened target list with the - * targetlist given by my caller, so that expressions are - * evaluated. - */ - subplan->targetlist = tlist; - } - - return subplan; -} - -/* - * subplanner - * - * Subplanner creates an entire plan consisting of joins and scans - * for processing a single level of attributes. - * - * flat_tlist is the flattened target list - * tuple_fraction is the fraction of tuples we expect will be retrieved - * - * See query_planner() comments about the interpretation of tuple_fraction. - * - * Returns a subplan. - */ -static Plan * -subplanner(Query *root, - List *flat_tlist, - double tuple_fraction) -{ - RelOptInfo *final_rel; - Plan *resultplan; - Path *cheapestpath; - Path *presortedpath; - - /* init lists to empty */ root->base_rel_list = NIL; root->other_rel_list = NIL; root->join_rel_list = NIL; @@ -197,8 +124,14 @@ subplanner(Query *root, * clauses are added to appropriate lists belonging to the mentioned * relations. We also build lists of equijoined keys for pathkey * construction. + * + * Note: all subplan nodes will have "flat" (var-only) tlists. + * This implies that all expression evaluations are done at the root of + * the plan tree. Once upon a time there was code to try to push + * expensive function calls down to lower plan nodes, but that's dead + * code and has been for a long time... */ - build_base_rel_tlists(root, flat_tlist); + build_base_rel_tlists(root, tlist); (void) distribute_quals_to_rels(root, (Node *) root->jointree); @@ -220,31 +153,8 @@ subplanner(Query *root, */ final_rel = make_one_rel(root); - if (!final_rel) - elog(ERROR, "subplanner: failed to construct a relation"); - -#ifdef NOT_USED /* fix xfunc */ - - /* - * Perform Predicate Migration on each path, to optimize and correctly - * assess the cost of each before choosing the cheapest one. -- JMH, - * 11/16/92 - * - * Needn't do so if the top rel is pruneable: that means there's no - * expensive functions left to pull up. -- JMH, 11/22/92 - */ - if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM && - XfuncMode != XFUNC_NOPULL && !final_rel->pruneable) - { - List *pathnode; - - foreach(pathnode, final_rel->pathlist) - { - if (xfunc_do_predmig((Path *) lfirst(pathnode))) - set_cheapest(final_rel); - } - } -#endif + if (!final_rel || !final_rel->cheapest_total_path) + elog(ERROR, "query_planner: failed to construct a relation"); /* * Now that we have an estimate of the final rel's size, we can @@ -255,75 +165,35 @@ subplanner(Query *root, 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. + * Pick out the cheapest-total path and the cheapest presorted path + * for the requested pathkeys (if there is one). We can take the + * tuple fraction into account when selecting the cheapest presorted + * path, but not when selecting the cheapest-total path, since if we + * have to sort then we'll have to fetch all the tuples. (But there's + * a special case: if query_pathkeys is NIL, meaning order doesn't + * matter, then the "cheapest presorted" path will be the cheapest + * overall for the tuple fraction.) */ - 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); + *cheapest_path = final_rel->cheapest_total_path; - 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, we use the cheapest path found above. - */ - if (root->query_pathkeys == NIL || - pathkeys_contained_in(root->query_pathkeys, - cheapestpath->pathkeys)) - { - root->query_pathkeys = cheapestpath->pathkeys; - resultplan = create_plan(root, cheapestpath); - goto plan_built; - } - - /* - * Otherwise, look to see if we have an already-ordered path that is - * cheaper than doing an explicit sort on the cheapest-total-cost - * path. - */ - cheapestpath = final_rel->cheapest_total_path; - presortedpath = + *sorted_path = get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist, root->query_pathkeys, tuple_fraction); - if (presortedpath) - { - Path sort_path; /* dummy for result of cost_sort */ - - cost_sort(&sort_path, root, root->query_pathkeys, - final_rel->rows, final_rel->width); - sort_path.startup_cost += cheapestpath->total_cost; - sort_path.total_cost += cheapestpath->total_cost; - if (compare_fractional_path_costs(presortedpath, &sort_path, - tuple_fraction) <= 0) - { - /* Presorted path is cheaper, use it */ - root->query_pathkeys = presortedpath->pathkeys; - resultplan = create_plan(root, presortedpath); - goto plan_built; - } - /* otherwise, doing it the hard way is still cheaper */ - } /* - * Nothing for it but to sort the cheapest-total-cost path --- but we - * let the caller do that. grouping_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...) + * If we have constant quals, add a toplevel Result step to process them. */ - root->query_pathkeys = cheapestpath->pathkeys; - resultplan = create_plan(root, cheapestpath); - -plan_built: - - return resultplan; + if (constant_quals) + { + *cheapest_path = (Path *) + create_result_path((*cheapest_path)->parent, + *cheapest_path, + constant_quals); + if (*sorted_path) + *sorted_path = (Path *) + create_result_path((*sorted_path)->parent, + *sorted_path, + constant_quals); + } } |