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


    继续分析 query_planner:

    /*
     * query_planner
     *      Generate a path (that is, a simplified plan) for a basic query,
     *      which may involve joins but not any fancier features.
     *
     * Since query_planner does not handle the toplevel processing (grouping,
     * sorting, etc) it cannot select the best path by itself.    It selects
     * two paths: the cheapest path that produces all the required tuples,
     * independent of any ordering considerations, and the cheapest path that
     * produces the expected fraction of the required tuples in the required
     * ordering, if there is a path that is cheaper for this than just sorting
     * the output of the cheapest overall path.  The caller (grouping_planner)
     * will make the final decision about which to use.
     *
     * Input parameters:
     * root describes the query to plan
     * tlist is the target list the query should produce
     *        (this is NOT necessarily root->parse->targetList!)
     * tuple_fraction is the fraction of tuples we expect will be retrieved
     * limit_tuples is a hard limit on number of tuples to retrieve,
     *        or -1 if no limit
     *
     * Output parameters:
     * *cheapest_path receives the overall-cheapest path for the query
     * *sorted_path receives the cheapest presorted path for the query,
     *                if any (NULL if there is no useful presorted path)
     * *num_groups receives the estimated number of groups, or 1 if query
     *                does not use grouping
     *
     * Note: the PlannerInfo node also includes a query_pathkeys field, which is
     * both an input and an output of query_planner().    The input value signals
     * query_planner that the indicated sort order is wanted in the final output
     * plan.  But this value has not yet been "canonicalized", since the needed
     * info does not get computed until we scan the qual clauses.  We canonicalize
     * it as soon as that task is done.  (The main reason query_pathkeys is a
     * PlannerInfo field and not a passed parameter is that the low-level routines
     * in indxpath.c need to see it.)
     *
     * Note: the PlannerInfo node includes other pathkeys fields besides
     * query_pathkeys, all of which need to be canonicalized once the info is
     * available.  See canonicalize_all_pathkeys.
     *
     * 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)
     * Note that a nonzero tuple_fraction could come from outer context; it is
     * therefore not redundant with limit_tuples.  We use limit_tuples to determine
     * whether a bounded sort can be used at runtime.
     */
    void
    query_planner(PlannerInfo *root, List *tlist,
                  double tuple_fraction, double limit_tuples,
                  Path **cheapest_path, Path **sorted_path,
                  double *num_groups)
    {
        Query       *parse = root->parse;
        List       *joinlist;
        RelOptInfo *final_rel;
        Path       *cheapestpath;
        Path       *sortedpath;
        Index        rti;
        double        total_pages;
    
        /* Make tuple_fraction, limit_tuples accessible to lower-level routines */
        root->tuple_fraction = tuple_fraction;
        root->limit_tuples = limit_tuples;
    
        *num_groups = 1;            /* default result */
    
        /*
         * If the query has an empty join tree, then it's something easy like
         * "SELECT 2+2;" or "INSERT ... VALUES()".    Fall through quickly.
         */
        if (parse->jointree->fromlist == NIL)
        {
            /* We need a trivial path result */
            *cheapest_path = (Path *)
                create_result_path((List *) parse->jointree->quals);
            *sorted_path = NULL;
    
            /*
             * We still are required to canonicalize any pathkeys, in case it's
             * something like "SELECT 2+2 ORDER BY 1".
             */
            root->canon_pathkeys = NIL;
            canonicalize_all_pathkeys(root);
            return;
        }
    
        /*
         * Init planner lists to empty.
         *
         * NOTE: append_rel_list was set up by subquery_planner, so do not touch
         * here; eq_classes and minmax_aggs may contain data already, too.
         */
        root->join_rel_list = NIL;
        root->join_rel_hash = NULL;
        root->join_rel_level = NULL;
        root->join_cur_level = 0;
        root->canon_pathkeys = NIL;
        root->left_join_clauses = NIL;
        root->right_join_clauses = NIL;
        root->full_join_clauses = NIL;
        root->join_info_list = NIL;
        root->placeholder_list = NIL;
        root->initial_rels = NIL;
    
        /*
         * Make a flattened version of the rangetable for faster access (this is
         * OK because the rangetable won't change any more), and set up an empty
         * array for indexing base relations.
         */
        setup_simple_rel_arrays(root);
    
        /*
         * Construct RelOptInfo nodes for all base relations in query, and
         * indirectly for all appendrel member relations ("other rels").  This
         * will give us a RelOptInfo for every "simple" (non-join) rel involved in
         * the query.
         *
         * Note: the reason we find the rels by searching the jointree and
         * appendrel list, rather than just scanning the rangetable, is that the
         * rangetable may contain RTEs for rels not actively part of the query,
         * for example views.  We don't want to make RelOptInfos for them.
         */
        add_base_rels_to_query(root, (Node *) parse->jointree);
    
        /*
         * Examine the targetlist and join tree, adding entries to baserel
         * targetlists for all referenced Vars, and generating PlaceHolderInfo
         * entries for all referenced PlaceHolderVars.    Restrict and join clauses
         * are added to appropriate lists belonging to the mentioned relations. We
         * also build EquivalenceClasses for provably equivalent expressions. The
         * SpecialJoinInfo list is also built to hold information about join order
         * restrictions.  Finally, we form a target joinlist for make_one_rel() to
         * work from.
         */
        build_base_rel_tlists(root, tlist);
    
        find_placeholders_in_jointree(root);
    
        joinlist = deconstruct_jointree(root);
    
        /*
         * Reconsider any postponed outer-join quals now that we have built up
         * equivalence classes.  (This could result in further additions or
         * mergings of classes.)
         */
        reconsider_outer_join_clauses(root);
    
        /*
         * If we formed any equivalence classes, generate additional restriction
         * clauses as appropriate.    (Implied join clauses are formed on-the-fly
         * later.)
         */
        generate_base_implied_equalities(root);
    
        /*
         * We have completed merging equivalence sets, so it's now possible to
         * convert previously generated pathkeys (in particular, the requested
         * query_pathkeys) to canonical form.
         */
        canonicalize_all_pathkeys(root);
    
        /*
         * Examine any "placeholder" expressions generated during subquery pullup.
         * Make sure that the Vars they need are marked as needed at the relevant
         * join level.    This must be done before join removal because it might
         * cause Vars or placeholders to be needed above a join when they weren't
         * so marked before.
         */
        fix_placeholder_input_needed_levels(root);
    
        /*
         * Remove any useless outer joins.    Ideally this would be done during
         * jointree preprocessing, but the necessary information isn't available
         * until we've built baserel data structures and classified qual clauses.
         */
        joinlist = remove_useless_joins(root, joinlist);
    
        /*
         * Now distribute "placeholders" to base rels as needed.  This has to be
         * done after join removal because removal could change whether a
         * placeholder is evaluatable at a base rel.
         */
        add_placeholders_to_base_rels(root);
    
        /*
         * We should now have size estimates for every actual table involved in
         * the query, and we also know which if any have been deleted from the
         * query by join removal; so we can compute total_table_pages.
         *
         * Note that appendrels are not double-counted here, even though we don't
         * bother to distinguish RelOptInfos for appendrel parents, because the
         * parents will still have size zero.
         *
         * XXX if a table is self-joined, we will count it once per appearance,
         * which perhaps is the wrong thing ... but that's not completely clear,
         * and detecting self-joins here is difficult, so ignore it for now.
         */
        total_pages = 0;
        for (rti = 1; rti < root->simple_rel_array_size; rti++)
        {
            RelOptInfo *brel = root->simple_rel_array[rti];
    
            if (brel == NULL)
                continue;
    
            Assert(brel->relid == rti);        /* sanity check on array */
    
            if (brel->reloptkind == RELOPT_BASEREL ||
                brel->reloptkind == RELOPT_OTHER_MEMBER_REL)
                total_pages += (double) brel->pages;
        }
        root->total_table_pages = total_pages;
    
        /*
         * Ready to do the primary planning.
         */
        final_rel = make_one_rel(root, joinlist);
    
        if (!final_rel || !final_rel->cheapest_total_path)
            elog(ERROR, "failed to construct the join relation");
    
        /*
         * If there's grouping going on, estimate the number of result groups. We
         * couldn't do this any earlier because it depends on relation size
         * estimates that were set up above.
         *
         * Then convert tuple_fraction to fractional form if it is absolute, and
         * adjust it based on the knowledge that grouping_planner will be doing
         * grouping or aggregation work with our result.
         *
         * This introduces some undesirable coupling between this code and
         * grouping_planner, but the alternatives seem even uglier; we couldn't
         * pass back completed paths without making these decisions here.
         */
        if (parse->groupClause)
        {
            List       *groupExprs;
    
            groupExprs = get_sortgrouplist_exprs(parse->groupClause,
                                                 parse->targetList);
            *num_groups = estimate_num_groups(root,
                                              groupExprs,
                                              final_rel->rows);
    
            /*
             * In GROUP BY mode, an absolute LIMIT is relative to the number of
             * groups not the number of tuples.  If the caller gave us a fraction,
             * keep it as-is.  (In both cases, we are effectively assuming that
             * all the groups are about the same size.)
             */
            if (tuple_fraction >= 1.0)
                tuple_fraction /= *num_groups;
    
            /*
             * If both GROUP BY and ORDER BY are specified, we will need two
             * levels of sort --- and, therefore, certainly need to read all the
             * tuples --- unless ORDER BY is a subset of GROUP BY.    Likewise if we
             * have both DISTINCT and GROUP BY, or if we have a window
             * specification not compatible with the GROUP BY.
             */
            if (!pathkeys_contained_in(root->sort_pathkeys, root->group_pathkeys) ||
                !pathkeys_contained_in(root->distinct_pathkeys, root->group_pathkeys) ||
             !pathkeys_contained_in(root->window_pathkeys, root->group_pathkeys))
                tuple_fraction = 0.0;
    
            /* In any case, limit_tuples shouldn't be specified here */
            Assert(limit_tuples < 0);
        }
        else if (parse->hasAggs || root->hasHavingQual)
        {
            /*
             * Ungrouped aggregate will certainly want to read all the tuples, and
             * it will deliver a single result row (so leave *num_groups 1).
             */
            tuple_fraction = 0.0;
    
            /* limit_tuples shouldn't be specified here */
            Assert(limit_tuples < 0);
        }
        else if (parse->distinctClause)
        {
            /*
             * Since there was no grouping or aggregation, it's reasonable to
             * assume the UNIQUE filter has effects comparable to GROUP BY. Return
             * the estimated number of output rows for use by caller. (If DISTINCT
             * is used with grouping, we ignore its effects for rowcount
             * estimation purposes; this amounts to assuming the grouped rows are
             * distinct already.)
             */
            List       *distinctExprs;
    
            distinctExprs = get_sortgrouplist_exprs(parse->distinctClause,
                                                    parse->targetList);
            *num_groups = estimate_num_groups(root,
                                              distinctExprs,
                                              final_rel->rows);
    
            /*
             * Adjust tuple_fraction the same way as for GROUP BY, too.
             */
            if (tuple_fraction >= 1.0)
                tuple_fraction /= *num_groups;
    
            /* limit_tuples shouldn't be specified here */
            Assert(limit_tuples < 0);
        }
        else
        {
            /*
             * Plain non-grouped, non-aggregated query: an absolute tuple fraction
             * can be divided by the number of tuples.
             */
            if (tuple_fraction >= 1.0)
                tuple_fraction /= final_rel->rows;
        }
    
        /*
         * Pick out the cheapest-total path and the cheapest presorted path for
         * the requested pathkeys (if there is one).  We should take the tuple
         * fraction into account when selecting the cheapest presorted path, but
         * not when selecting the cheapest-total path, since if we have to sort
         * then we'll have to fetch all the tuples.  (But there's a special case:
         * if query_pathkeys is NIL, meaning order doesn't matter, then the
         * "cheapest presorted" path will be the cheapest overall for the tuple
         * fraction.)
         *
         * The cheapest-total path is also the one to use if grouping_planner
         * decides to use hashed aggregation, so we return it separately even if
         * this routine thinks the presorted path is the winner.
         */
        cheapestpath = final_rel->cheapest_total_path;
    
        sortedpath =
            get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
                                                      root->query_pathkeys,
                                                      NULL,
                                                      tuple_fraction);
    
        /* Don't return same path in both guises; just wastes effort */
        if (sortedpath == cheapestpath)
            sortedpath = NULL;
    
        /*
         * Forget about the presorted path if it would be cheaper to sort the
         * cheapest-total path.  Here we need consider only the behavior at the
         * tuple fraction point.
         */
        if (sortedpath)
        {
            Path        sort_path;    /* dummy for result of cost_sort */
    
            if (root->query_pathkeys == NIL ||
                pathkeys_contained_in(root->query_pathkeys,
                                      cheapestpath->pathkeys))
            {
                /* No sort needed for cheapest path */
                sort_path.startup_cost = cheapestpath->startup_cost;
                sort_path.total_cost = cheapestpath->total_cost;
            }
            else
            {
                /* Figure cost for sorting */
                cost_sort(&sort_path, root, root->query_pathkeys,
                          cheapestpath->total_cost,
                          final_rel->rows, final_rel->width,
                          0.0, work_mem, limit_tuples);
            }
    
            if (compare_fractional_path_costs(sortedpath, &sort_path,
                                              tuple_fraction) > 0)
            {
                /* Presorted path is a loser */
                sortedpath = NULL;
            }
        }
    
        *cheapest_path = cheapestpath;
        *sorted_path = sortedpath;
    }

    这个就是对基本的查询的基本分析了。

    下面,慢慢进行:

    我的简单查询里面,有 from 子句,所以不满足如下这段,不会直接退出。

        /*
         * If the query has an empty join tree, then it's something easy like
         * "SELECT 2+2;" or "INSERT ... VALUES()".    Fall through quickly.
         */
        if (parse->jointree->fromlist == NIL)
        {
            /* We need a trivial path result */
            *cheapest_path = (Path *)
                create_result_path((List *) parse->jointree->quals);
            *sorted_path = NULL;
    
            /*
             * We still are required to canonicalize any pathkeys, in case it's
             * something like "SELECT 2+2 ORDER BY 1".
             */
            root->canon_pathkeys = NIL;
            canonicalize_all_pathkeys(root);
            return;
        }

    下面是初始化动作:

        /*
         * Init planner lists to empty.
         *
         * NOTE: append_rel_list was set up by subquery_planner, so do not touch
         * here; eq_classes and minmax_aggs may contain data already, too.
         */
        root->join_rel_list = NIL;
        root->join_rel_hash = NULL;
        root->join_rel_level = NULL;
        root->join_cur_level = 0;
        root->canon_pathkeys = NIL;
        root->left_join_clauses = NIL;
        root->right_join_clauses = NIL;
        root->full_join_clauses = NIL;
        root->join_info_list = NIL;
        root->placeholder_list = NIL;
        root->initial_rels = NIL;

    下面这一段,也先把它当作一个简单的初始化理解:

        /*
         * Make a flattened version of the rangetable for faster access (this is
         * OK because the rangetable won't change any more), and set up an empty
         * array for indexing base relations.
         */
        setup_simple_rel_arrays(root);

    接着,这是把 from 和 where 条件的 FromExpr型指针,变换为Node型指针,挂到 root上。

        /*
         * Construct RelOptInfo nodes for all base relations in query, and
         * indirectly for all appendrel member relations ("other rels").  This
         * will give us a RelOptInfo for every "simple" (non-join) rel involved in
         * the query.
         *
         * Note: the reason we find the rels by searching the jointree and
         * appendrel list, rather than just scanning the rangetable, is that the
         * rangetable may contain RTEs for rels not actively part of the query,
         * for example views.  We don't want to make RelOptInfos for them.
         */
        add_base_rels_to_query(root, (Node *) parse->jointree);

    将其展开:

    /*****************************************************************************
     *
     *     JOIN TREES
     *
     *****************************************************************************/
    
    /*
     * add_base_rels_to_query
     *
     *      Scan the query's jointree and create baserel RelOptInfos for all
     *      the base relations (ie, table, subquery, and function RTEs)
     *      appearing in the jointree.
     *
     * The initial invocation must pass root->parse->jointree as the value of
     * jtnode.    Internally, the function recurses through the jointree.
     *
     * At the end of this process, there should be one baserel RelOptInfo for
     * every non-join RTE that is used in the query.  Therefore, this routine
     * is the only place that should call build_simple_rel with reloptkind
     * RELOPT_BASEREL.    (Note: build_simple_rel recurses internally to build
     * "other rel" RelOptInfos for the members of any appendrels we find here.)
     */
    void
    add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
    {
        if (jtnode == NULL)
            return;
        if (IsA(jtnode, RangeTblRef))
        {
            int            varno = ((RangeTblRef *) jtnode)->rtindex;
    
            (void) build_simple_rel(root, varno, RELOPT_BASEREL);
        }
        else if (IsA(jtnode, FromExpr))
        {
            FromExpr   *f = (FromExpr *) jtnode;
            ListCell   *l;
    
            foreach(l, f->fromlist)
                add_base_rels_to_query(root, lfirst(l));
        }
        else if (IsA(jtnode, JoinExpr))
        {
            JoinExpr   *j = (JoinExpr *) jtnode;
    
            add_base_rels_to_query(root, j->larg);
            add_base_rels_to_query(root, j->rarg);
        }
        else
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(jtnode));
    }

    这里面,有递归调用。

    主要是因为这个:就是把from 里面每个表明,都挂在root上。

            foreach(l, f->fromlist)
                add_base_rels_to_query(root, lfirst(l));

    上述的递归调用时,进了  if (IsA(jtnode, RangeTblRef)) 的判断。会调用 build_simple_rel函数,展开了看:

    /*
     * build_simple_rel
     *      Construct a new RelOptInfo for a base relation or 'other' relation.
     */
    RelOptInfo *
    build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
    {
        RelOptInfo *rel;
        RangeTblEntry *rte;
    
        /* Rel should not exist already */
        Assert(relid > 0 && relid < root->simple_rel_array_size);
        if (root->simple_rel_array[relid] != NULL)
            elog(ERROR, "rel %d already exists", relid);
    
        /* Fetch RTE for relation */
        rte = root->simple_rte_array[relid];
        Assert(rte != NULL);
    
        rel = makeNode(RelOptInfo);
        rel->reloptkind = reloptkind;
        rel->relids = bms_make_singleton(relid);
        rel->rows = 0;
        rel->width = 0;
        rel->reltargetlist = NIL;
        rel->pathlist = NIL;
        rel->ppilist = NIL;
        rel->cheapest_startup_path = NULL;
        rel->cheapest_total_path = NULL;
        rel->cheapest_unique_path = NULL;
        rel->cheapest_parameterized_paths = NIL;
        rel->relid = relid;
        rel->rtekind = rte->rtekind;
        /* min_attr, max_attr, attr_needed, attr_widths are set below */
        rel->indexlist = NIL;
        rel->pages = 0;
        rel->tuples = 0;
        rel->allvisfrac = 0;
        rel->subplan = NULL;
        rel->subroot = NULL;
        rel->fdwroutine = NULL;
        rel->fdw_private = NULL;
        rel->baserestrictinfo = NIL;
        rel->baserestrictcost.startup = 0;
        rel->baserestrictcost.per_tuple = 0;
        rel->joininfo = NIL;
        rel->has_eclass_joins = false;
    
        /* Check type of rtable entry */
        switch (rte->rtekind)
        {
            case RTE_RELATION:
                /* Table --- retrieve statistics from the system catalogs */
                get_relation_info(root, rte->relid, rte->inh, rel);
                break;
            case RTE_SUBQUERY:
            case RTE_FUNCTION:
            case RTE_VALUES:
            case RTE_CTE:
    
                /*
                 * Subquery, function, or values list --- set up attr range and
                 * arrays
                 *
                 * Note: 0 is included in range to support whole-row Vars
                 */
                rel->min_attr = 0;
                rel->max_attr = list_length(rte->eref->colnames);
                rel->attr_needed = (Relids *)
                    palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
                rel->attr_widths = (int32 *)
                    palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));
                break;
            default:
                elog(ERROR, "unrecognized RTE kind: %d",
                     (int) rte->rtekind);
                break;
        }
    
        /* Save the finished struct in the query's simple_rel_array */
        root->simple_rel_array[relid] = rel;
    
        /*
         * If this rel is an appendrel parent, recurse to build "other rel"
         * RelOptInfos for its children.  They are "other rels" because they are
         * not in the main join tree, but we will need RelOptInfos to plan access
         * to them.
         */
        if (rte->inh)
        {
            ListCell   *l;
    
            foreach(l, root->append_rel_list)
            {
                AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
    
                /* append_rel_list contains all append rels; ignore others */
                if (appinfo->parent_relid != relid)
                    continue;
    
                (void) build_simple_rel(root, appinfo->child_relid,
                                        RELOPT_OTHER_MEMBER_REL);
            }
        }
    
        return rel;
    }

    核心是这个:

        /* Save the finished struct in the query's simple_rel_array */
        root->simple_rel_array[relid] = rel;

    回到 query_planner 这个层面。

        /*
         * Examine the targetlist and join tree, adding entries to baserel
         * targetlists for all referenced Vars, and generating PlaceHolderInfo
         * entries for all referenced PlaceHolderVars.    Restrict and join clauses
         * are added to appropriate lists belonging to the mentioned relations. We
         * also build EquivalenceClasses for provably equivalent expressions. The
         * SpecialJoinInfo list is also built to hold information about join order
         * restrictions.  Finally, we form a target joinlist for make_one_rel() to
         * work from.
         */
        build_base_rel_tlists(root, tlist);

    展开:

    这个和我的简单查询关系也不大,因为我的简单查询没有什么表达式要计算:

    /*
     * build_base_rel_tlists
     *      Add targetlist entries for each var needed in the query's final tlist
     *      to the appropriate base relations.
     *
     * We mark such vars as needed by "relation 0" to ensure that they will
     * propagate up through all join plan steps.
     */
    void
    build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
    {
        List       *tlist_vars = pull_var_clause((Node *) final_tlist,
                                                 PVC_RECURSE_AGGREGATES,
                                                 PVC_INCLUDE_PLACEHOLDERS);
    
        if (tlist_vars != NIL)
        {
            add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true);
            list_free(tlist_vars);
        }
    }

    那么下面这一段:

    find_placeholders_in_jointree(root);

    展开后可以看到,对于我的简单查询,没有 placeholderz,所以会直接返回:

    /*
     * find_placeholders_in_jointree
     *        Search the jointree for PlaceHolderVars, and build PlaceHolderInfos
     *
     * We don't need to look at the targetlist because build_base_rel_tlists()
     * will already have made entries for any PHVs in the tlist.
     */
    void
    find_placeholders_in_jointree(PlannerInfo *root)
    {
    
        /* We need do nothing if the query contains no PlaceHolderVars */
        if (root->glob->lastPHId != 0)
        {
            /* Start recursion at top of jointree */
            Assert(root->parse->jointree != NULL &&
                   IsA(root->parse->jointree, FromExpr));
            (void) find_placeholders_recurse(root, (Node *) root->parse->jointree);
        }
    }

    接着往下看,这个先暂时无视。

        joinlist = deconstruct_jointree(root);

    接着:由于我的是简单查询,如下的两段,可以被无视。

        /*
         * Reconsider any postponed outer-join quals now that we have built up
         * equivalence classes.  (This could result in further additions or
         * mergings of classes.)
         */
        reconsider_outer_join_clauses(root);
    
        /*
         * If we formed any equivalence classes, generate additional restriction
         * clauses as appropriate.    (Implied join clauses are formed on-the-fly
         * later.)
         */
        generate_base_implied_equalities(root);

    再接着看下面这一段好了:

        /*
         * We have completed merging equivalence sets, so it's now possible to
         * convert previously generated pathkeys (in particular, the requested
         * query_pathkeys) to canonical form.
         */
        canonicalize_all_pathkeys(root);

    展开:

    /*
     * canonicalize_all_pathkeys
     *        Canonicalize all pathkeys that were generated before entering
     *        query_planner and then stashed in PlannerInfo.
     */
    static void
    canonicalize_all_pathkeys(PlannerInfo *root)
    {
        root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys);
        root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys);
        root->window_pathkeys = canonicalize_pathkeys(root, root->window_pathkeys);
        root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
        root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys);
    }
    /*
     * canonicalize_pathkeys
     *       Convert a not-necessarily-canonical pathkeys list to canonical form.
     *
     * Note that this function must not be used until after we have completed
     * merging EquivalenceClasses.
     */
    List *
    canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
    {
        List       *new_pathkeys = NIL;
        ListCell   *l;
    
        foreach(l, pathkeys)
        {
            PathKey    *pathkey = (PathKey *) lfirst(l);
            EquivalenceClass *eclass;
            PathKey    *cpathkey;
    
            /* Find the canonical (merged) EquivalenceClass */
            eclass = pathkey->pk_eclass;
            while (eclass->ec_merged)
                eclass = eclass->ec_merged;
    
            /*
             * If we can tell it's redundant just from the EC, skip.
             * pathkey_is_redundant would notice that, but we needn't even bother
             * constructing the node...
             */
            if (EC_MUST_BE_REDUNDANT(eclass))
                continue;
    
            /* OK, build a canonicalized PathKey struct */
            cpathkey = make_canonical_pathkey(root,
                                              eclass,
                                              pathkey->pk_opfamily,
                                              pathkey->pk_strategy,
                                              pathkey->pk_nulls_first);
    
            /* Add to list unless redundant */
            if (!pathkey_is_redundant(cpathkey, new_pathkeys))
                new_pathkeys = lappend(new_pathkeys, cpathkey);
        }
        return new_pathkeys;
    }

    到这一步,心里有个感触,是否我需要把所有的 sql文再复习一遍?

    由于我的简单查询 root->query_pathkeys 是NG, 所以就直接返回了。

    接着再分析下一个调用:

    这下面三段,都可以无视:

        /*
         * Examine any "placeholder" expressions generated during subquery pullup.
         * Make sure that the Vars they need are marked as needed at the relevant
         * join level.    This must be done before join removal because it might
         * cause Vars or placeholders to be needed above a join when they weren't
         * so marked before.
         */
        fix_placeholder_input_needed_levels(root);
    
        /*
         * Remove any useless outer joins.    Ideally this would be done during
         * jointree preprocessing, but the necessary information isn't available
         * until we've built baserel data structures and classified qual clauses.
         */
        joinlist = remove_useless_joins(root, joinlist);
    
        /*
         * Now distribute "placeholders" to base rels as needed.  This has to be
         * done after join removal because removal could change whether a
         * placeholder is evaluatable at a base rel.
         */
        add_placeholders_to_base_rels(root);

    再接着分析:

        /*
         * We should now have size estimates for every actual table involved in
         * the query, and we also know which if any have been deleted from the
         * query by join removal; so we can compute total_table_pages.
         *
         * Note that appendrels are not double-counted here, even though we don't
         * bother to distinguish RelOptInfos for appendrel parents, because the
         * parents will still have size zero.
         *
         * XXX if a table is self-joined, we will count it once per appearance,
         * which perhaps is the wrong thing ... but that's not completely clear,
         * and detecting self-joins here is difficult, so ignore it for now.
         */
        total_pages = 0;
        for (rti = 1; rti < root->simple_rel_array_size; rti++)
        {
            RelOptInfo *brel = root->simple_rel_array[rti];
    
            if (brel == NULL)
                continue;
    
            Assert(brel->relid == rti);        /* sanity check on array */
    
            if (brel->reloptkind == RELOPT_BASEREL ||
                brel->reloptkind == RELOPT_OTHER_MEMBER_REL)
                total_pages += (double) brel->pages;
        }
        root->total_table_pages = total_pages;

    这是说,要得到总共的 页面数,好作为计划的依据。

    但是我执行到此处,令我惊诧的是,得到的 root->total_table_pages 居然是0!

    接下来的应该算是戏肉吧:

        /*
         * Ready to do the primary planning.
         */
        final_rel = make_one_rel(root, joinlist);
  • 相关阅读:
    利用高斯金字塔制作图像显著图
    python实现图像高斯金字塔
    使用差分金字塔提取图像边缘 python实现
    使用色彩追踪和形态学运算得到图像中感兴趣区域
    图像掩膜讲解
    图像色彩追踪
    手动绘制方向梯度直方图(HOG)
    Zhang-Suen 图像细化算法python实现
    hilditch图像细化算法python实现
    P2256 一中校运会之百米跑
  • 原文地址:https://www.cnblogs.com/gaojian/p/3118992.html
Copyright © 2020-2023  润新知