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

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介:

接前面:

复制代码
    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;
复制代码

对此进行一步一步地分析:

对我的简单查询,因为 条件不成立,以下这段被无视:

复制代码
    /* 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;
    }
复制代码

接下来:由于我的简单查询非 set 运算,所以简化为:

复制代码
    if (parse->setOperations)
    {
        ...
    }
    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) */
复制代码

还是太复杂,进一步分析 else分支:

因为没 group by ,所以 这一小段无视:

        /* Preprocess GROUP BY clause, if any */
        if (parse->groupClause)
            preprocess_groupclause(root);
        numGroupCols = list_length(parse->groupClause);

下面的:

        /* Preprocess targetlist */
        tlist = preprocess_targetlist(root, tlist);

我如果用  select  id, val from tst04 where id >1 ,则tlist 的长度是 2。
我如果用  select  id  from tst04 where id> 1,则 tlist 的长度是1。
我如果用  select  * from tst04 where id>1 ,则 tlist 长度也是2(tst04共有两个字段)。

也就是说, preprocess_targetlist 取得的是 sql查询应当返回的 列名list。

接着,因为没有使用窗口函数,如下这一段也可以无视:

复制代码
        /*
         * 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);

和前面的  preprocess_targetlist 非常类似。 

接下来,因为没使用聚集函数,所以也可无视:

复制代码
        /*
         * 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;
复制代码

得到  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);
复制代码

以上,root->window_pathkeys =NIL,root->distinct_pathkeys=NIL。

而 root->sort_pathkeys 的长度为 0。

接下来,下面这段,导致  root->query_pathkeys = NIL。

复制代码
        /*
         * 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;
复制代码

 接下来,因为我的简单查询没有 limit,所以:sub_limit_tuples = limit_tuples=-1

复制代码
        /*
         * 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);
复制代码
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
10天前
|
SQL 监控 关系型数据库
SQL语句当前及历史信息查询-performance schema的使用
本文介绍了如何使用MySQL的Performance Schema来获取SQL语句的当前和历史执行信息。Performance Schema默认在MySQL 8.0中启用,可以通过查询相关表来获取详细的SQL执行信息,包括当前执行的SQL、历史执行记录和统计汇总信息,从而快速定位和解决性能瓶颈。
|
21天前
|
SQL 存储 缓存
如何优化SQL查询性能?
【10月更文挑战第28天】如何优化SQL查询性能?
75 10
|
15天前
|
SQL 关系型数据库 MySQL
|
24天前
|
SQL 关系型数据库 数据库
PostgreSQL性能飙升的秘密:这几个调优技巧让你的数据库查询速度翻倍!
【10月更文挑战第25天】本文介绍了几种有效提升 PostgreSQL 数据库查询效率的方法,包括索引优化、查询优化、配置优化和硬件优化。通过合理设计索引、编写高效 SQL 查询、调整配置参数和选择合适硬件,可以显著提高数据库性能。
143 1
|
29天前
|
SQL 数据库 开发者
功能发布-自定义SQL查询
本期主要为大家介绍ClkLog九月上线的新功能-自定义SQL查询。
|
1月前
|
SQL 移动开发 Oracle
SQL语句实现查询连续六天数据的方法与技巧
在数据库查询中,有时需要筛选出符合特定时间连续性条件的数据记录
|
1月前
|
SQL Java 数据库连接
如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
【10月更文挑战第6天】在代码与逻辑交织的世界中,我从一名数据库新手出发,通过不断探索与实践,最终成为熟练掌握JDBC的开发者。这段旅程充满挑战与惊喜,从建立数据库连接到执行SQL语句,再到理解事务管理和批处理等高级功能,每一步都让我对JDBC有了更深的认识。示例代码展示了如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
98 5
|
25天前
|
SQL 关系型数据库 MySQL
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
31 0
|
1月前
|
SQL 数据可视化 BI
SQL语句及查询结果解析:技巧与方法
在数据库管理和数据分析中,SQL语句扮演着至关重要的角色
|
1月前
|
SQL 监控 关系型数据库
使用SQL语句查询操作耗时的技巧与方法
在数据库管理和优化过程中,了解SQL查询操作的耗时是至关重要的
下一篇
无影云桌面