PostgreSQL在何处处理 sql查询之五十三

接前面。

从PostgreSQL的 log中,看到 计划树中有一个  plan_rows的东西。

分析它的来源和来龙去脉:

grouping_planner --> create_plan --> create_plan_recurse --> create_scan_plan

--> create_seqscan_plan --> copy_path_costsize

而copy_path_costsize 中:

/*

 * Copy cost and size info from a Path node to the Plan node created from it.

 * The executor usually won't use this info, but it's needed by EXPLAIN.

 */

static void

copy_path_costsize(Plan *dest, Path *src)

{

    fprintf(stderr,"In copy_path_costsize\n");



    if (src)

    {

        fprintf(stderr,"In src \n\n");



        dest->startup_cost = src->startup_cost;

        dest->total_cost = src->total_cost;

        dest->plan_rows = src->rows;

        dest->plan_width = src->parent->width;

    }

    else

    {

        fprintf(stderr,"In not src \n\n");



        dest->startup_cost = 0;

        dest->total_cost = 0;

        dest->plan_rows = 0;

        dest->plan_width = 0;

    }

}

其中,这一段是其作用的:

    if (src)

    {

        dest->startup_cost = src->startup_cost;

        dest->total_cost = src->total_cost;

        dest->plan_rows = src->rows;

        dest->plan_width = src->parent->width;

    }

上溯一层:create_seqscan_plan:  copy_path_costsize 入口参数的 src,就是 create-seqscan_plan 入口 的best_path。

/*

 * create_seqscan_plan

 *     Returns a seqscan plan for the base relation scanned by 'best_path'

 *     with restriction clauses 'scan_clauses' and targetlist 'tlist'.

 */

static SeqScan *

create_seqscan_plan(PlannerInfo *root, Path *best_path,

                    List *tlist, List *scan_clauses)

{

    //fprintf(stderr,"xxx In create_seqscan_plan\n");



    SeqScan    *scan_plan;

    Index        scan_relid = best_path->parent->relid;



    /* it should be a base rel... */

    Assert(scan_relid > 0);

    Assert(best_path->parent->rtekind == RTE_RELATION);



    /* Sort clauses into best execution order */

    scan_clauses = order_qual_clauses(root, scan_clauses);



    /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */

    scan_clauses = extract_actual_clauses(scan_clauses, false);



    /* Replace any outer-relation variables with nestloop params */

    if (best_path->param_info)

    {

        scan_clauses = (List *)

            replace_nestloop_params(root, (Node *) scan_clauses);

    }



    scan_plan = make_seqscan(tlist,

                             scan_clauses,

                             scan_relid);



    copy_path_costsize(&scan_plan->plan, best_path);



    return scan_plan;

}

再上溯一层:create_scan_plan: create_seqscan_plan的 best_path 来自于create_scan_plan的入口参数 best_path。

/*

 * create_scan_plan

 *     Create a scan plan for the parent relation of 'best_path'.

 */

static Plan *

create_scan_plan(PlannerInfo *root, Path *best_path)

{

    fprintf(stderr, "xxx In create_scan_plan\n");



    RelOptInfo *rel = best_path->parent;

    List       *tlist;

    List       *scan_clauses;

    Plan       *plan;



    /*

     * For table scans, rather than using the relation targetlist (which is

     * only those Vars actually needed by the query), we prefer to generate a

     * tlist containing all Vars in order.    This will allow the executor to

     * optimize away projection of the table tuples, if possible.  (Note that

     * planner.c may replace the tlist we generate here, forcing projection to

     * occur.)

     */

    if (use_physical_tlist(root, rel))

    {

        if (best_path->pathtype == T_IndexOnlyScan)

        {

            /* For index-only scan, the preferred tlist is the index's */

            tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);

        }

        else

        {

            tlist = build_physical_tlist(root, rel);

            /* if fail because of dropped cols, use regular method */

            if (tlist == NIL)

                tlist = build_relation_tlist(rel);

        }

    }

    else

        tlist = build_relation_tlist(rel);



    /*

     * Extract the relevant restriction clauses from the parent relation. The

     * executor must apply all these restrictions during the scan, except for

     * pseudoconstants which we'll take care of below.

     */

    scan_clauses = rel->baserestrictinfo;



    /*

     * If this is a parameterized scan, we also need to enforce all the join

     * clauses available from the outer relation(s).

     *

     * For paranoia's sake, don't modify the stored baserestrictinfo list.

     */

    if (best_path->param_info)

        scan_clauses = list_concat(list_copy(scan_clauses),

                                   best_path->param_info->ppi_clauses);



    switch (best_path->pathtype)

    {

        case T_SeqScan:

            plan = (Plan *) create_seqscan_plan(root, best_path, tlist, scan_clauses); break;



        case T_IndexScan:

            plan = (Plan *) create_indexscan_plan(root,

                                                  (IndexPath *) best_path,

                                                  tlist,

                                                  scan_clauses,

                                                  false);

            break;



        case T_IndexOnlyScan:

            plan = (Plan *) create_indexscan_plan(root,

                                                  (IndexPath *) best_path,

                                                  tlist,

                                                  scan_clauses,

                                                  true);

            break;



        case T_BitmapHeapScan:

            plan = (Plan *) create_bitmap_scan_plan(root,

                                                (BitmapHeapPath *) best_path,

                                                    tlist,

                                                    scan_clauses);

            break;



        case T_TidScan:

            plan = (Plan *) create_tidscan_plan(root,

                                                (TidPath *) best_path,

                                                tlist,

                                                scan_clauses);

            break;



        case T_SubqueryScan:

            plan = (Plan *) create_subqueryscan_plan(root,

                                                     best_path,

                                                     tlist,

                                                     scan_clauses);

            break;



        case T_FunctionScan:

            plan = (Plan *) create_functionscan_plan(root,

                                                     best_path,

                                                     tlist,

                                                     scan_clauses);

            break;



        case T_ValuesScan:

            plan = (Plan *) create_valuesscan_plan(root,

                                                   best_path,

                                                   tlist,

                                                   scan_clauses);

            break;



        case T_CteScan:

            plan = (Plan *) create_ctescan_plan(root,

                                                best_path,

                                                tlist,

                                                scan_clauses);

            break;



        case T_WorkTableScan:

            plan = (Plan *) create_worktablescan_plan(root,

                                                      best_path,

                                                      tlist,

                                                      scan_clauses);

            break;



        case T_ForeignScan:

            plan = (Plan *) create_foreignscan_plan(root,

                                                    (ForeignPath *) best_path,

                                                    tlist,

                                                    scan_clauses);

            break;



        default:

            elog(ERROR, "unrecognized node type: %d",

                 (int) best_path->pathtype);

            plan = NULL;        /* keep compiler quiet */

            break;

    }



    /*

     * If there are any pseudoconstant clauses attached to this node, insert a

     * gating Result node that evaluates the pseudoconstants as one-time

     * quals.

     */

    if (root->hasPseudoConstantQuals)

        plan = create_gating_plan(root, plan, scan_clauses);



    return plan;

}

再上溯:create_plan_recurse: 入口参数里已经带入了 best_path

/*

 * create_plan_recurse

 *      Recursive guts of create_plan().

 */

static Plan *

create_plan_recurse(PlannerInfo *root, Path *best_path)

{



    fprintf(stderr,"xxx In create_plan_recurse\n");



    Plan       *plan;



    switch (best_path->pathtype)

    {

        case T_SeqScan:

        case T_IndexScan:

        case T_IndexOnlyScan:

        case T_BitmapHeapScan:

        case T_TidScan:

        case T_SubqueryScan:

        case T_FunctionScan:

        case T_ValuesScan:

        case T_CteScan:

        case T_WorkTableScan:

        case T_ForeignScan:

            plan = create_scan_plan(root, best_path); break;

        case T_HashJoin:

        case T_MergeJoin:

        case T_NestLoop:

            plan = create_join_plan(root,

                                    (JoinPath *) best_path);

            break;

        case T_Append:

            plan = create_append_plan(root,

                                      (AppendPath *) best_path);

            break;

        case T_MergeAppend:

            plan = create_merge_append_plan(root,

                                            (MergeAppendPath *) best_path);

            break;

        case T_Result:

            plan = (Plan *) create_result_plan(root,

                                               (ResultPath *) best_path);

            break;

        case T_Material:

            plan = (Plan *) create_material_plan(root,

                                                 (MaterialPath *) best_path);

            break;

        case T_Unique:

            plan = create_unique_plan(root,

                                      (UniquePath *) best_path);

            break;

        default:

            elog(ERROR, "unrecognized node type: %d",

                 (int) best_path->pathtype);

            plan = NULL;        /* keep compiler quiet */

            break;

    }



    return plan;

}

再次上溯:create_plan: 入口参数里已经带入了 best_path

/*

 * create_plan

 *      Creates the access plan for a query by recursively processing the

 *      desired tree of pathnodes, starting at the node 'best_path'.    For

 *      every pathnode found, we create a corresponding plan node containing

 *      appropriate id, target list, and qualification information.

 *

 *      The tlists and quals in the plan tree are still in planner format,

 *      ie, Vars still correspond to the parser's numbering.  This will be

 *      fixed later by setrefs.c.

 *

 *      best_path is the best access path

 *

 *      Returns a Plan tree.

 */

Plan *

create_plan(PlannerInfo *root, Path *best_path)

{



    fprintf(stderr,"xxx In create_plan \n");



    Plan       *plan;



    /* plan_params should not be in use in current query level */

    Assert(root->plan_params == NIL);



    /* Initialize this module's private workspace in PlannerInfo */

    root->curOuterRels = NULL;

    root->curOuterParams = NIL;



    /* Recursively process the path tree */

    plan = create_plan_recurse(root, best_path);



    /* Check we successfully assigned all NestLoopParams to plan nodes */

    if (root->curOuterParams != NIL)

        elog(ERROR, "failed to assign all NestLoopParams to plan nodes");



    /*

     * Reset plan_params to ensure param IDs used for nestloop params are not

     * re-used later

     */

    root->plan_params = NIL;



    return plan;

}

再上溯:grouping_planner:

/*--------------------

 * grouping_planner

 *      Perform planning steps related to grouping, aggregation, etc.

 *      This primarily means adding top-level processing to the basic

 *      query plan produced by query_planner.

 *

 * tuple_fraction is the fraction of tuples we expect will be retrieved

 *

 * tuple_fraction is interpreted as follows:

 *      0: 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)

 *

 * Returns a query plan.  Also, root->query_pathkeys is returned as the

 * actual output ordering of the plan (in pathkey format).

 *--------------------

 */

static Plan *

grouping_planner(PlannerInfo *root, double tuple_fraction)

{



    fprintf(stderr,"xxx In grouping_planner\n");



    Query       *parse = root->parse;

    List       *tlist = parse->targetList;

    int64        offset_est = 0;

    int64        count_est = 0;

    double        limit_tuples = -1.0;

    Plan       *result_plan;

    List       *current_pathkeys;

    double        dNumGroups = 0;

    bool        use_hashed_distinct = false;

    bool        tested_hashed_distinct = false;





    /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */

    if (parse->limitCount || parse->limitOffset)

    {

        tuple_fraction = preprocess_limit(root, tuple_fraction,

                                          &offset_est, &count_est);



        /*

         * If we have a known LIMIT, and don't have an unknown OFFSET, we can

         * estimate the effects of using a bounded sort.

         */

        if (count_est > 0 && offset_est >= 0)

            limit_tuples = (double) count_est + (double) offset_est;

    }



    if (parse->setOperations)

    {



        List       *set_sortclauses;



        /*

         * If there's a top-level ORDER BY, assume we have to fetch all the

         * tuples.    This might be too simplistic given all the hackery below

         * to possibly avoid the sort; but the odds of accurate estimates here

         * are pretty low anyway.

         */

        if (parse->sortClause)

            tuple_fraction = 0.0;



        /*

         * Construct the plan for set operations.  The result will not need

         * any work except perhaps a top-level sort and/or LIMIT.  Note that

         * any special work for recursive unions is the responsibility of

         * plan_set_operations.

         */

        result_plan = plan_set_operations(root, tuple_fraction,

                                          &set_sortclauses);



        /*

         * Calculate pathkeys representing the sort order (if any) of the set

         * operation's result.  We have to do this before overwriting the sort

         * key information...

         */

        current_pathkeys = make_pathkeys_for_sortclauses(root,

                                                         set_sortclauses,

                                                     result_plan->targetlist,

                                                         true);



        /*

         * We should not need to call preprocess_targetlist, since we must be

         * in a SELECT query node.    Instead, use the targetlist returned by

         * plan_set_operations (since this tells whether it returned any

         * resjunk columns!), and transfer any sort key information from the

         * original tlist.

         */

        Assert(parse->commandType == CMD_SELECT);



        tlist = postprocess_setop_tlist(copyObject(result_plan->targetlist),

                                        tlist);



        /*

         * Can't handle FOR UPDATE/SHARE here (parser should have checked

         * already, but let's make sure).

         */

        if (parse->rowMarks)

            ereport(ERROR,

                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),

                     errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));



        /*

         * Calculate pathkeys that represent result ordering requirements

         */

        Assert(parse->distinctClause == NIL);

        root->sort_pathkeys = make_pathkeys_for_sortclauses(root,

                                                            parse->sortClause,

                                                            tlist,

                                                            true);

    }

    else

    {



        /* No set operations, do regular planning */

        List       *sub_tlist;

        double        sub_limit_tuples;

        AttrNumber *groupColIdx = NULL;

        bool        need_tlist_eval = true;

        Path       *cheapest_path;

        Path       *sorted_path;

        Path       *best_path;

        long        numGroups = 0;

        AggClauseCosts agg_costs;

        int            numGroupCols;

        double        path_rows;

        int            path_width;

        bool        use_hashed_grouping = false;

        WindowFuncLists *wflists = NULL;

        List       *activeWindows = NIL;



        MemSet(&agg_costs, 0, sizeof(AggClauseCosts));



        /* A recursive query should always have setOperations */

        Assert(!root->hasRecursion);



        /* Preprocess GROUP BY clause, if any */

        if (parse->groupClause)

            preprocess_groupclause(root);

        numGroupCols = list_length(parse->groupClause);



        /* Preprocess targetlist */

        tlist = preprocess_targetlist(root, tlist);



        /*

         * Locate any window functions in the tlist.  (We don't need to look

         * anywhere else, since expressions used in ORDER BY will be in there

         * too.)  Note that they could all have been eliminated by constant

         * folding, in which case we don't need to do any more work.

         */

        if (parse->hasWindowFuncs)

        {

            wflists = find_window_functions((Node *) tlist,

                                            list_length(parse->windowClause));

            if (wflists->numWindowFuncs > 0)

                activeWindows = select_active_windows(root, wflists);

            else

                parse->hasWindowFuncs = false;

        }



        /*

         * Generate appropriate target list for subplan; may be different from

         * tlist if grouping or aggregation is needed.

         */

        sub_tlist = make_subplanTargetList(root, tlist,

                                           &groupColIdx, &need_tlist_eval);



        /*

         * Do aggregate preprocessing, if the query has any aggs.

         *

         * Note: think not that we can turn off hasAggs if we find no aggs. It

         * is possible for constant-expression simplification to remove all

         * explicit references to aggs, but we still have to follow the

         * aggregate semantics (eg, producing only one output row).

         */

        if (parse->hasAggs)

        {

            /*

             * Collect statistics about aggregates for estimating costs. Note:

             * we do not attempt to detect duplicate aggregates here; a

             * somewhat-overestimated cost is okay for our present purposes.

             */

            count_agg_clauses(root, (Node *) tlist, &agg_costs);

            count_agg_clauses(root, parse->havingQual, &agg_costs);



            /*

             * Preprocess MIN/MAX aggregates, if any.  Note: be careful about

             * adding logic between here and the optimize_minmax_aggregates

             * call.  Anything that is needed in MIN/MAX-optimizable cases

             * will have to be duplicated in planagg.c.

             */

            preprocess_minmax_aggregates(root, tlist);

        }



        /*

         * Calculate pathkeys that represent grouping/ordering requirements.

         * Stash them in PlannerInfo so that query_planner can canonicalize

         * them after EquivalenceClasses have been formed.    The sortClause is

         * certainly sort-able, but GROUP BY and DISTINCT might not be, in

         * which case we just leave their pathkeys empty.

         */

        if (parse->groupClause &&

            grouping_is_sortable(parse->groupClause))

            root->group_pathkeys =

                make_pathkeys_for_sortclauses(root,

                                              parse->groupClause,

                                              tlist,

                                              false);

        else

            root->group_pathkeys = NIL;







        /* We consider only the first (bottom) window in pathkeys logic */

        if (activeWindows != NIL)

        {

            WindowClause *wc = (WindowClause *) linitial(activeWindows);



            root->window_pathkeys = make_pathkeys_for_window(root,

                                                             wc,

                                                             tlist,

                                                             false);

        }

        else

            root->window_pathkeys = NIL;



        if (parse->distinctClause &&

            grouping_is_sortable(parse->distinctClause))

            root->distinct_pathkeys =

                make_pathkeys_for_sortclauses(root,

                                              parse->distinctClause,

                                              tlist,

                                              false);

        else

            root->distinct_pathkeys = NIL;



        root->sort_pathkeys =

            make_pathkeys_for_sortclauses(root,

                                          parse->sortClause,

                                          tlist,

                                          false);



        /*

         * Figure out whether we want a sorted result from query_planner.

         *

         * If we have a sortable GROUP BY clause, then we want a result sorted

         * properly for grouping.  Otherwise, if we have window functions to

         * evaluate, we try to sort for the first window.  Otherwise, if

         * there's a sortable DISTINCT clause that's more rigorous than the

         * ORDER BY clause, we try to produce output that's sufficiently well

         * sorted for the DISTINCT.  Otherwise, if there is an ORDER BY

         * clause, we want to sort by the ORDER BY clause.

         *

         * Note: if we have both ORDER BY and GROUP BY, 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.    The choice for DISTINCT

         * versus ORDER BY is much easier, since we know that the parser

         * ensured that one is a superset of the other.

         */

        if (root->group_pathkeys)

            root->query_pathkeys = root->group_pathkeys;

        else if (root->window_pathkeys)

            root->query_pathkeys = root->window_pathkeys;

        else if (list_length(root->distinct_pathkeys) >

                 list_length(root->sort_pathkeys))

            root->query_pathkeys = root->distinct_pathkeys;

        else if (root->sort_pathkeys)

            root->query_pathkeys = root->sort_pathkeys;

        else

            root->query_pathkeys = NIL;





        /*

         * Figure out whether there's a hard limit on the number of rows that

         * query_planner's result subplan needs to return.  Even if we know a

         * hard limit overall, it doesn't apply if the query has any

         * grouping/aggregation operations.

         */

        if (parse->groupClause ||

            parse->distinctClause ||

            parse->hasAggs ||

            parse->hasWindowFuncs ||

            root->hasHavingQual)

            sub_limit_tuples = -1.0;

        else

            sub_limit_tuples = limit_tuples;



        /*

         * Generate the best unsorted and presorted paths for this Query (but

         * note there may not be any presorted path).  query_planner will also

         * estimate the number of groups in the query, and canonicalize all

         * the pathkeys.

         */

        query_planner(root, sub_tlist, tuple_fraction, sub_limit_tuples,

                      &cheapest_path, &sorted_path, &dNumGroups);



        /*

         * Extract rowcount and width estimates for possible use in grouping

         * decisions.  Beware here of the possibility that

         * cheapest_path->parent is NULL (ie, there is no FROM clause).

         */

        if (cheapest_path->parent)

        {

            path_rows = cheapest_path->parent->rows;

            path_width = cheapest_path->parent->width;

        }

        else

        {

            path_rows = 1;        /* assume non-set result */

            path_width = 100;    /* arbitrary */

        }



        if (parse->groupClause)

        {

            /*

             * If grouping, decide whether to use sorted or hashed grouping.

             */

            use_hashed_grouping =

                choose_hashed_grouping(root,

                                       tuple_fraction, limit_tuples,

                                       path_rows, path_width,

                                       cheapest_path, sorted_path,

                                       dNumGroups, &agg_costs);

            /* Also convert # groups to long int --- but 'ware overflow! */

            numGroups = (long) Min(dNumGroups, (double) LONG_MAX);

        }

        else if (parse->distinctClause && sorted_path &&

                 !root->hasHavingQual && !parse->hasAggs && !activeWindows)

        {

            /*

             * We'll reach the DISTINCT stage without any intermediate

             * processing, so figure out whether we will want to hash or not

             * so we can choose whether to use cheapest or sorted path.

             */

            use_hashed_distinct =

                choose_hashed_distinct(root,

                                       tuple_fraction, limit_tuples,

                                       path_rows, path_width,

                                       cheapest_path->startup_cost,

                                       cheapest_path->total_cost,

                                       sorted_path->startup_cost,

                                       sorted_path->total_cost,

                                       sorted_path->pathkeys,

                                       dNumGroups);

            tested_hashed_distinct = true;

        }



        /*

         * Select the best path.  If we are doing hashed grouping, we will

         * always read all the input tuples, so use the cheapest-total path.

         * Otherwise, trust query_planner's decision about which to use.

         */

        if (use_hashed_grouping || use_hashed_distinct || !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,

                                                 &agg_costs,

                                                 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.

             */

            bool        need_sort_for_grouping = false;



            result_plan = create_plan(root, best_path);

            current_pathkeys = best_path->pathkeys;



            /* Detect if we'll need an explicit sort for grouping */

            if (parse->groupClause && !use_hashed_grouping &&

              !pathkeys_contained_in(root->group_pathkeys, current_pathkeys))

            {

                need_sort_for_grouping = true;



                /*

                 * Always override create_plan's tlist, so that we don't sort

                 * useless data from a "physical" tlist.

                 */

                need_tlist_eval = true;

            }



            /*

             * 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 create_plan 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(root,

                                                       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.

                 * See comments for add_tlist_costs_to_plan() for more info.

                 */

                add_tlist_costs_to_plan(root, result_plan, sub_tlist);

            }

            else

            {

                /*

                 * Since we're using create_plan'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,

                                                &agg_costs,

                                                numGroupCols,

                                                groupColIdx,

                                    extract_grouping_ops(parse->groupClause),

                                                numGroups,

                                                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 (need_sort_for_grouping)

                    {

                        result_plan = (Plan *)

                            make_sort_from_groupcols(root,

                                                     parse->groupClause,

                                                     groupColIdx,

                                                     result_plan);

                        current_pathkeys = root->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,

                                                &agg_costs,

                                                numGroupCols,

                                                groupColIdx,

                                    extract_grouping_ops(parse->groupClause),

                                                numGroups,

                                                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 (need_sort_for_grouping)

                {

                    result_plan = (Plan *)

                        make_sort_from_groupcols(root,

                                                 parse->groupClause,

                                                 groupColIdx,

                                                 result_plan);

                    current_pathkeys = root->group_pathkeys;

                }



                result_plan = (Plan *) make_group(root,

                                                  tlist,

                                                  (List *) parse->havingQual,

                                                  numGroupCols,

                                                  groupColIdx,

                                    extract_grouping_ops(parse->groupClause),

                                                  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(root,

                                                   tlist,

                                                   parse->havingQual,

                                                   NULL);

            }

        }                        /* end of non-minmax-aggregate case */



        /*

         * Since each window function could require a different sort order, we

         * stack up a WindowAgg node for each window, with sort steps between

         * them as needed.

         */

        if (activeWindows)

        {

            List       *window_tlist;

            ListCell   *l;



            /*

             * If the top-level plan node is one that cannot do expression

             * evaluation, we must insert a Result node to project the desired

             * tlist.  (In some cases this might not really be required, but

             * it's not worth trying to avoid it.)  Note that on second and

             * subsequent passes through the following loop, the top-level

             * node will be a WindowAgg which we know can project; so we only

             * need to check once.

             */

            if (!is_projection_capable_plan(result_plan))

            {

                result_plan = (Plan *) make_result(root,

                                                   NIL,

                                                   NULL,

                                                   result_plan);

            }



            /*

             * The "base" targetlist for all steps of the windowing process is

             * a flat tlist of all Vars and Aggs needed in the result.  (In

             * some cases we wouldn't need to propagate all of these all the

             * way to the top, since they might only be needed as inputs to

             * WindowFuncs.  It's probably not worth trying to optimize that

             * though.)  We also add window partitioning and sorting

             * expressions to the base tlist, to ensure they're computed only

             * once at the bottom of the stack (that's critical for volatile

             * functions).  As we climb up the stack, we'll add outputs for

             * the WindowFuncs computed at each level.

             */

            window_tlist = make_windowInputTargetList(root,

                                                      tlist,

                                                      activeWindows);



            /*

             * The copyObject steps here are needed to ensure that each plan

             * node has a separately modifiable tlist.  (XXX wouldn't a

             * shallow list copy do for that?)

             */

            result_plan->targetlist = (List *) copyObject(window_tlist);



            foreach(l, activeWindows)

            {

                WindowClause *wc = (WindowClause *) lfirst(l);

                List       *window_pathkeys;

                int            partNumCols;

                AttrNumber *partColIdx;

                Oid           *partOperators;

                int            ordNumCols;

                AttrNumber *ordColIdx;

                Oid           *ordOperators;



                window_pathkeys = make_pathkeys_for_window(root,

                                                           wc,

                                                           tlist,

                                                           true);



                /*

                 * This is a bit tricky: we build a sort node even if we don't

                 * really have to sort.  Even when no explicit sort is needed,

                 * we need to have suitable resjunk items added to the input

                 * plan's tlist for any partitioning or ordering columns that

                 * aren't plain Vars.  (In theory, make_windowInputTargetList

                 * should have provided all such columns, but let's not assume

                 * that here.)  Furthermore, this way we can use existing

                 * infrastructure to identify which input columns are the

                 * interesting ones.

                 */

                if (window_pathkeys)

                {

                    Sort       *sort_plan;



                    sort_plan = make_sort_from_pathkeys(root,

                                                        result_plan,

                                                        window_pathkeys,

                                                        -1.0);

                    if (!pathkeys_contained_in(window_pathkeys,

                                               current_pathkeys))

                    {

                        /* we do indeed need to sort */

                        result_plan = (Plan *) sort_plan;

                        current_pathkeys = window_pathkeys;

                    }

                    /* In either case, extract the per-column information */

                    get_column_info_for_window(root, wc, tlist,

                                               sort_plan->numCols,

                                               sort_plan->sortColIdx,

                                               &partNumCols,

                                               &partColIdx,

                                               &partOperators,

                                               &ordNumCols,

                                               &ordColIdx,

                                               &ordOperators);

                }

                else

                {

                    /* empty window specification, nothing to sort */

                    partNumCols = 0;

                    partColIdx = NULL;

                    partOperators = NULL;

                    ordNumCols = 0;

                    ordColIdx = NULL;

                    ordOperators = NULL;

                }



                if (lnext(l))

                {

                    /* Add the current WindowFuncs to the running tlist */

                    window_tlist = add_to_flat_tlist(window_tlist,

                                           wflists->windowFuncs[wc->winref]);

                }

                else

                {

                    /* Install the original tlist in the topmost WindowAgg */

                    window_tlist = tlist;

                }



                /* ... and make the WindowAgg plan node */

                result_plan = (Plan *)

                    make_windowagg(root,

                                   (List *) copyObject(window_tlist),

                                   wflists->windowFuncs[wc->winref],

                                   wc->winref,

                                   partNumCols,

                                   partColIdx,

                                   partOperators,

                                   ordNumCols,

                                   ordColIdx,

                                   ordOperators,

                                   wc->frameOptions,

                                   wc->startOffset,

                                   wc->endOffset,

                                   result_plan);

            }

        }

    }                            /* end of if (setOperations) */



    /*

     * If there is a DISTINCT clause, add the necessary node(s).

     */

    if (parse->distinctClause)

    {

        double        dNumDistinctRows;

        long        numDistinctRows;



        /*

         * If there was grouping or aggregation, use the current number of

         * rows as the estimated number of DISTINCT rows (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)

            dNumDistinctRows = result_plan->plan_rows;

        else

            dNumDistinctRows = dNumGroups;



        /* Also convert to long int --- but 'ware overflow! */

        numDistinctRows = (long) Min(dNumDistinctRows, (double) LONG_MAX);



        /* Choose implementation method if we didn't already */

        if (!tested_hashed_distinct)

        {

            /*

             * At this point, either hashed or sorted grouping will have to

             * work from result_plan, so we pass that as both "cheapest" and

             * "sorted".

             */

            use_hashed_distinct =

                choose_hashed_distinct(root,

                                       tuple_fraction, limit_tuples,

                                       result_plan->plan_rows,

                                       result_plan->plan_width,

                                       result_plan->startup_cost,

                                       result_plan->total_cost,

                                       result_plan->startup_cost,

                                       result_plan->total_cost,

                                       current_pathkeys,

                                       dNumDistinctRows);

        }



        if (use_hashed_distinct)

        {

            /* Hashed aggregate plan --- no sort needed */

            result_plan = (Plan *) make_agg(root,

                                            result_plan->targetlist,

                                            NIL,

                                            AGG_HASHED,

                                            NULL,

                                          list_length(parse->distinctClause),

                                 extract_grouping_cols(parse->distinctClause,

                                                    result_plan->targetlist),

                                 extract_grouping_ops(parse->distinctClause),

                                            numDistinctRows,

                                            result_plan);

            /* Hashed aggregation produces randomly-ordered results */

            current_pathkeys = NIL;

        }

        else

        {

            /*

             * Use a Unique node to implement DISTINCT.  Add an explicit sort

             * if we couldn't make the path come out the way the Unique node

             * needs it.  If we do have to sort, always sort by the more

             * rigorous of DISTINCT and ORDER BY, to avoid a second sort

             * below.  However, for regular DISTINCT, don't sort now if we

             * don't have to --- sorting afterwards will likely be cheaper,

             * and also has the possibility of optimizing via LIMIT.  But for

             * DISTINCT ON, we *must* force the final sort now, else it won't

             * have the desired behavior.

             */

            List       *needed_pathkeys;



            if (parse->hasDistinctOn &&

                list_length(root->distinct_pathkeys) <

                list_length(root->sort_pathkeys))

                needed_pathkeys = root->sort_pathkeys;

            else

                needed_pathkeys = root->distinct_pathkeys;



            if (!pathkeys_contained_in(needed_pathkeys, current_pathkeys))

            {

                if (list_length(root->distinct_pathkeys) >=

                    list_length(root->sort_pathkeys))

                    current_pathkeys = root->distinct_pathkeys;

                else

                {

                    current_pathkeys = root->sort_pathkeys;

                    /* Assert checks that parser didn't mess up... */

                    Assert(pathkeys_contained_in(root->distinct_pathkeys,

                                                 current_pathkeys));

                }



                result_plan = (Plan *) make_sort_from_pathkeys(root,

                                                               result_plan,

                                                            current_pathkeys,

                                                               -1.0);

            }



            result_plan = (Plan *) make_unique(result_plan,

                                               parse->distinctClause);

            result_plan->plan_rows = dNumDistinctRows;

            /* The Unique node won't change sort ordering */

        }

    }



    /*

     * If ORDER BY was given and 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(root->sort_pathkeys, current_pathkeys))

        {

            result_plan = (Plan *) make_sort_from_pathkeys(root,

                                                           result_plan,

                                                         root->sort_pathkeys,

                                                           limit_tuples);

            current_pathkeys = root->sort_pathkeys;

        }

    }



    /*

     * If there is a FOR UPDATE/SHARE clause, add the LockRows node. (Note: we

     * intentionally test parse->rowMarks not root->rowMarks here. If there

     * are only non-locking rowmarks, they should be handled by the

     * ModifyTable node instead.)

     */

    if (parse->rowMarks)

    {

        result_plan = (Plan *) make_lockrows(result_plan,

                                             root->rowMarks,

                                             SS_assign_special_param(root));



        /*

         * The result can no longer be assumed sorted, since locking might

         * cause the sort key columns to be replaced with new values.

         */

        current_pathkeys = NIL;

    }



    /*

     * 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;

}

上面的 grouping_planner简化一下:

static Plan *

grouping_planner(PlannerInfo *root, double tuple_fraction)

{

    ...

       if (parse->setOperations)

    {

        ...

    }

    else

    {

        ...

        Path       *cheapest_path;

        Path       *sorted_path;

        Path       *best_path;        

        ...

        /*

         * Select the best path.  If we are doing hashed grouping, we will

         * always read all the input tuples, so use the cheapest-total path.

         * Otherwise, trust query_planner's decision about which to use.

         */

        if (use_hashed_grouping || use_hashed_distinct || !sorted_path)

            best_path = cheapest_path;

        else

            best_path = sorted_path;

         ...

     }

     ...

}

 以我的最简单的查询而言,是不会有 sorted_path的。

而cheapest_path 的来源是,上述代码中:

        /*

         * Generate the best unsorted and presorted paths for this Query (but

         * note there may not be any presorted path).  query_planner will also

         * estimate the number of groups in the query, and canonicalize all

         * the pathkeys.

         */

        query_planner(root, sub_tlist, tuple_fraction, sub_limit_tuples,

                      &cheapest_path, &sorted_path, &dNumGroups);



        ...



        if (parse->groupClause)

        {

            /*

             * If grouping, decide whether to use sorted or hashed grouping.

             */

            use_hashed_grouping =

                choose_hashed_grouping(root,

                                       tuple_fraction, limit_tuples,

                                       path_rows, path_width,

                                       cheapest_path, sorted_path,

                                       dNumGroups, &agg_costs);

            /* Also convert # groups to long int --- but 'ware overflow! */

            numGroups = (long) Min(dNumGroups, (double) LONG_MAX);

        }

        else if (parse->distinctClause && sorted_path &&

                 !root->hasHavingQual && !parse->hasAggs && !activeWindows)

        {

            /*

             * We'll reach the DISTINCT stage without any intermediate

             * processing, so figure out whether we will want to hash or not

             * so we can choose whether to use cheapest or sorted path.

             */

            use_hashed_distinct =

                choose_hashed_distinct(root,

                                       tuple_fraction, limit_tuples,

                                       path_rows, path_width,

                                       cheapest_path->startup_cost,

                                       cheapest_path->total_cost,

                                       sorted_path->startup_cost,

                                       sorted_path->total_cost,

                                       sorted_path->pathkeys,

                                       dNumGroups);

            tested_hashed_distinct = true;

        }

对我的简单查询,只关心 query_planner 函数就行了。

 

 

 

 

你可能感兴趣的:(PostgreSQL)