PostgreSQL在何处处理 sql查询之四十七

再次上溯:

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

 * subquery_planner

 *      Invokes the planner on a subquery.  We recurse to here for each

 *      sub-SELECT found in the query tree.

 *

 * glob is the global state for the current planner run.

 * parse is the querytree produced by the parser & rewriter.

 * parent_root is the immediate parent Query's info (NULL at the top level).

 * hasRecursion is true if this is a recursive WITH query.

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

 * tuple_fraction is interpreted as explained for grouping_planner, below.

 *

 * If subroot isn't NULL, we pass back the query's final PlannerInfo struct;

 * among other things this tells the output sort ordering of the plan.

 *

 * Basically, this routine does the stuff that should only be done once

 * per Query object.  It then calls grouping_planner.  At one time,

 * grouping_planner could be invoked recursively on the same Query object;

 * that's not currently true, but we keep the separation between the two

 * routines anyway, in case we need it again someday.

 *

 * subquery_planner will be called recursively to handle sub-Query nodes

 * found within the query's expressions and rangetable.

 *

 * Returns a query plan.

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

 */

Plan *

subquery_planner(PlannerGlobal *glob, Query *parse,

                 PlannerInfo *parent_root,

                 bool hasRecursion, double tuple_fraction,

                 PlannerInfo **subroot)

{

    int            num_old_subplans = list_length(glob->subplans);

    PlannerInfo *root;

    Plan       *plan;

    List       *newHaving;

    bool        hasOuterJoins;

    ListCell   *l;



    /* Create a PlannerInfo data structure for this subquery */

    root = makeNode(PlannerInfo);

    root->parse = parse;

    root->glob = glob;

    root->query_level = parent_root ? parent_root->query_level + 1 : 1;

    root->parent_root = parent_root;

    root->plan_params = NIL;

    root->planner_cxt = CurrentMemoryContext;

    root->init_plans = NIL;

    root->cte_plan_ids = NIL;

    root->eq_classes = NIL;

    root->append_rel_list = NIL;

    root->rowMarks = NIL;

    root->hasInheritedTarget = false;



    root->hasRecursion = hasRecursion;

    if (hasRecursion)

        root->wt_param_id = SS_assign_special_param(root);

    else

        root->wt_param_id = -1;

    root->non_recursive_plan = NULL;



    /*

     * If there is a WITH list, process each WITH query and build an initplan

     * SubPlan structure for it.

     */

    if (parse->cteList)

        SS_process_ctes(root);



    /*

     * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try

     * to transform them into joins.  Note that this step does not descend

     * into subqueries; if we pull up any subqueries below, their SubLinks are

     * processed just before pulling them up.

     */

    if (parse->hasSubLinks)

        pull_up_sublinks(root);



    /*

     * Scan the rangetable for set-returning functions, and inline them if

     * possible (producing subqueries that might get pulled up next).

     * Recursion issues here are handled in the same way as for SubLinks.

     */

    inline_set_returning_functions(root);



    /*

     * Check to see if any subqueries in the jointree can be merged into this

     * query.

     */

    parse->jointree = (FromExpr *)

        pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);



    /*

     * If this is a simple UNION ALL query, flatten it into an appendrel. We

     * do this now because it requires applying pull_up_subqueries to the leaf

     * queries of the UNION ALL, which weren't touched above because they

     * weren't referenced by the jointree (they will be after we do this).

     */

    if (parse->setOperations)

        flatten_simple_union_all(root);



    /*

     * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can

     * avoid the expense of doing flatten_join_alias_vars().  Also check for

     * outer joins --- if none, we can skip reduce_outer_joins(). This must be

     * done after we have done pull_up_subqueries, of course.

     */

    root->hasJoinRTEs = false;

    hasOuterJoins = false;

    foreach(l, parse->rtable)

    {

        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);



        if (rte->rtekind == RTE_JOIN)

        {

            root->hasJoinRTEs = true;

            if (IS_OUTER_JOIN(rte->jointype))

            {

                hasOuterJoins = true;

                /* Can quit scanning once we find an outer join */

                break;

            }

        }

    }



    /*

     * Preprocess RowMark information.    We need to do this after subquery

     * pullup (so that all non-inherited RTEs are present) and before

     * inheritance expansion (so that the info is available for

     * expand_inherited_tables to examine and modify).

     */

    preprocess_rowmarks(root);



    /*

     * Expand any rangetable entries that are inheritance sets into "append

     * relations".  This can add entries to the rangetable, but they must be

     * plain base relations not joins, so it's OK (and marginally more

     * efficient) to do it after checking for join RTEs.  We must do it after

     * pulling up subqueries, else we'd fail to handle inherited tables in

     * subqueries.

     */

    expand_inherited_tables(root);



    /*

     * Set hasHavingQual to remember if HAVING clause is present.  Needed

     * because preprocess_expression will reduce a constant-true condition to

     * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.

     */

    root->hasHavingQual = (parse->havingQual != NULL);



    /* Clear this flag; might get set in distribute_qual_to_rels */

    root->hasPseudoConstantQuals = false;



    /*

     * Do expression preprocessing on targetlist and quals, as well as other

     * random expressions in the querytree.  Note that we do not need to

     * handle sort/group expressions explicitly, because they are actually

     * part of the targetlist.

     */

    parse->targetList = (List *)

        preprocess_expression(root, (Node *) parse->targetList,

                              EXPRKIND_TARGET);



    parse->returningList = (List *)

        preprocess_expression(root, (Node *) parse->returningList,

                              EXPRKIND_TARGET);



    preprocess_qual_conditions(root, (Node *) parse->jointree);



    parse->havingQual = preprocess_expression(root, parse->havingQual,

                                              EXPRKIND_QUAL);



    foreach(l, parse->windowClause)

    {

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



        /* partitionClause/orderClause are sort/group expressions */

        wc->startOffset = preprocess_expression(root, wc->startOffset,

                                                EXPRKIND_LIMIT);

        wc->endOffset = preprocess_expression(root, wc->endOffset,

                                              EXPRKIND_LIMIT);

    }



    parse->limitOffset = preprocess_expression(root, parse->limitOffset,

                                               EXPRKIND_LIMIT);

    parse->limitCount = preprocess_expression(root, parse->limitCount,

                                              EXPRKIND_LIMIT);



    root->append_rel_list = (List *)

        preprocess_expression(root, (Node *) root->append_rel_list,

                              EXPRKIND_APPINFO);



    /* Also need to preprocess expressions for function and values RTEs */

    foreach(l, parse->rtable)

    {

        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);



        if (rte->rtekind == RTE_FUNCTION)

            rte->funcexpr = preprocess_expression(root, rte->funcexpr,

                                                  EXPRKIND_RTFUNC);

        else if (rte->rtekind == RTE_VALUES)

            rte->values_lists = (List *)

                preprocess_expression(root, (Node *) rte->values_lists,

                                      EXPRKIND_VALUES);

    }



    /*

     * In some cases we may want to transfer a HAVING clause into WHERE. We

     * cannot do so if the HAVING clause contains aggregates (obviously) or

     * volatile functions (since a HAVING clause is supposed to be executed

     * only once per group).  Also, it may be that the clause is so expensive

     * to execute that we're better off doing it only once per group, despite

     * the loss of selectivity.  This is hard to estimate short of doing the

     * entire planning process twice, so we use a heuristic: clauses

     * containing subplans are left in HAVING.    Otherwise, we move or copy the

     * HAVING clause into WHERE, in hopes of eliminating tuples before

     * aggregation instead of after.

     *

     * If the query has explicit grouping then we can simply move such a

     * clause into WHERE; any group that fails the clause will not be in the

     * output because none of its tuples will reach the grouping or

     * aggregation stage.  Otherwise we must have a degenerate (variable-free)

     * HAVING clause, which we put in WHERE so that query_planner() can use it

     * in a gating Result node, but also keep in HAVING to ensure that we

     * don't emit a bogus aggregated row. (This could be done better, but it

     * seems not worth optimizing.)

     *

     * Note that both havingQual and parse->jointree->quals are in

     * implicitly-ANDed-list form at this point, even though they are declared

     * as Node *.

     */

    newHaving = NIL;

    foreach(l, (List *) parse->havingQual)

    {

        Node       *havingclause = (Node *) lfirst(l);



        if (contain_agg_clause(havingclause) ||

            contain_volatile_functions(havingclause) ||

            contain_subplans(havingclause))

        {

            /* keep it in HAVING */

            newHaving = lappend(newHaving, havingclause);

        }

        else if (parse->groupClause)

        {

            /* move it to WHERE */

            parse->jointree->quals = (Node *)

                lappend((List *) parse->jointree->quals, havingclause);

        }

        else

        {

            /* put a copy in WHERE, keep it in HAVING */

            parse->jointree->quals = (Node *)

                lappend((List *) parse->jointree->quals,

                        copyObject(havingclause));

            newHaving = lappend(newHaving, havingclause);

        }

    }

    parse->havingQual = (Node *) newHaving;



    /*

     * If we have any outer joins, try to reduce them to plain inner joins.

     * This step is most easily done after we've done expression

     * preprocessing.

     */

    if (hasOuterJoins)

        reduce_outer_joins(root);



    /*

     * Do the main planning.  If we have an inherited target relation, that

     * needs special processing, else go straight to grouping_planner.

     */

    if (parse->resultRelation &&

        rt_fetch(parse->resultRelation, parse->rtable)->inh)

        plan = inheritance_planner(root);

    else

    {

        plan = grouping_planner(root, tuple_fraction); /* If it's not SELECT, we need a ModifyTable node */

        if (parse->commandType != CMD_SELECT)

        {

            List       *returningLists;

            List       *rowMarks;



            /*

             * Set up the RETURNING list-of-lists, if needed.

             */

            if (parse->returningList)

                returningLists = list_make1(parse->returningList);

            else

                returningLists = NIL;



            /*

             * If there was a FOR UPDATE/SHARE clause, the LockRows node will

             * have dealt with fetching non-locked marked rows, else we need

             * to have ModifyTable do that.

             */

            if (parse->rowMarks)

                rowMarks = NIL;

            else

                rowMarks = root->rowMarks;



            plan = (Plan *) make_modifytable(parse->commandType,

                                             parse->canSetTag,

                                       list_make1_int(parse->resultRelation),

                                             list_make1(plan),

                                             returningLists,

                                             rowMarks,

                                             SS_assign_special_param(root));

        }

    }



    /*

     * If any subplans were generated, or if there are any parameters to worry

     * about, build initPlan list and extParam/allParam sets for plan nodes,

     * and attach the initPlans to the top plan node.

     */

    if (list_length(glob->subplans) != num_old_subplans ||

        root->glob->nParamExec > 0)

        SS_finalize_plan(root, plan, true);



    /* Return internal info if caller wants it */

    if (subroot)

        *subroot = root;



    return plan;

}

上溯

PlannedStmt *

standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)

{

    PlannedStmt *result;

    PlannerGlobal *glob;

    double        tuple_fraction;

    PlannerInfo *root;

    Plan       *top_plan;

    ListCell   *lp,

               *lr;



    /* Cursor options may come from caller or from DECLARE CURSOR stmt */

    if (parse->utilityStmt &&

        IsA(parse->utilityStmt, DeclareCursorStmt))

        cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;



    /*

     * Set up global state for this planner invocation.  This data is needed

     * across all levels of sub-Query that might exist in the given command,

     * so we keep it in a separate struct that's linked to by each per-Query

     * PlannerInfo.

     */

    glob = makeNode(PlannerGlobal);



    glob->boundParams = boundParams;

    glob->subplans = NIL;

    glob->subroots = NIL;

    glob->rewindPlanIDs = NULL;

    glob->finalrtable = NIL;

    glob->finalrowmarks = NIL;

    glob->resultRelations = NIL;

    glob->relationOids = NIL;

    glob->invalItems = NIL;

    glob->nParamExec = 0;

    glob->lastPHId = 0;

    glob->lastRowMarkId = 0;

    glob->transientPlan = false;



    /* Determine what fraction of the plan is likely to be scanned */

    if (cursorOptions & CURSOR_OPT_FAST_PLAN)

    {

        /*

         * We have no real idea how many tuples the user will ultimately FETCH

         * from a cursor, but it is often the case that he doesn't want 'em

         * all, or would prefer a fast-start plan anyway so that he can

         * process some of the tuples sooner.  Use a GUC parameter to decide

         * what fraction to optimize for.

         */

        tuple_fraction = cursor_tuple_fraction;



        /*

         * We document cursor_tuple_fraction as simply being a fraction, which

         * means the edge cases 0 and 1 have to be treated specially here.    We

         * convert 1 to 0 ("all the tuples") and 0 to a very small fraction.

         */

        if (tuple_fraction >= 1.0)

            tuple_fraction = 0.0;

        else if (tuple_fraction <= 0.0)

            tuple_fraction = 1e-10;

    }

    else

    {

        /* Default assumption is we need all the tuples */

        tuple_fraction = 0.0;

    }



    /* primary planning entry point (may recurse for subqueries) */

    top_plan = subquery_planner(glob, parse, NULL,

                                false, tuple_fraction, &root);



    /*

     * If creating a plan for a scrollable cursor, make sure it can run

     * backwards on demand.  Add a Material node at the top at need.

     */

    if (cursorOptions & CURSOR_OPT_SCROLL)

    {

        if (!ExecSupportsBackwardScan(top_plan))

            top_plan = materialize_finished_plan(top_plan);

    }



    /* final cleanup of the plan */

    Assert(glob->finalrtable == NIL);

    Assert(glob->finalrowmarks == NIL);

    Assert(glob->resultRelations == NIL);

    top_plan = set_plan_references(root, top_plan);

    /* ... and the subplans (both regular subplans and initplans) */

    Assert(list_length(glob->subplans) == list_length(glob->subroots));

    forboth(lp, glob->subplans, lr, glob->subroots)

    {

        Plan       *subplan = (Plan *) lfirst(lp);

        PlannerInfo *subroot = (PlannerInfo *) lfirst(lr);



        lfirst(lp) = set_plan_references(subroot, subplan);

    }



    /* build the PlannedStmt result */

    result = makeNode(PlannedStmt);



    result->commandType = parse->commandType;

    result->queryId = parse->queryId;

    result->hasReturning = (parse->returningList != NIL);

    result->hasModifyingCTE = parse->hasModifyingCTE;

    result->canSetTag = parse->canSetTag;

    result->transientPlan = glob->transientPlan;

    result->planTree = top_plan;

    result->rtable = glob->finalrtable;

    result->resultRelations = glob->resultRelations;

    result->utilityStmt = parse->utilityStmt;

    result->subplans = glob->subplans;

    result->rewindPlanIDs = glob->rewindPlanIDs;

    result->rowMarks = glob->finalrowmarks;

    result->relationOids = glob->relationOids;

    result->invalItems = glob->invalItems;

    result->nParamExec = glob->nParamExec;



    return result;

}

 再上溯:

/*****************************************************************************

 *

 *       Query optimizer entry point

 *

 * To support loadable plugins that monitor or modify planner behavior,

 * we provide a hook variable that lets a plugin get control before and

 * after the standard planning process.  The plugin would normally call

 * standard_planner().

 *

 * Note to plugin authors: standard_planner() scribbles on its Query input,

 * so you'd better copy that data structure if you want to plan more than once.

 *

 *****************************************************************************/

PlannedStmt *

planner(Query *parse, int cursorOptions, ParamListInfo boundParams)

{

    PlannedStmt *result;



    if (planner_hook)

        result = (*planner_hook) (parse, cursorOptions, boundParams);

    else

        result = standard_planner(parse, cursorOptions, boundParams);

    return result;

}

对 standard_planner 分析:

/*

 * Query -

 *      Parse analysis turns all statements into a Query tree

 *      for further processing by the rewriter and planner.

 *

 *      Utility statements (i.e. non-optimizable statements) have the

 *      utilityStmt field set, and the Query itself is mostly dummy.

 *      DECLARE CURSOR is a special case: it is represented like a SELECT,

 *      but the original DeclareCursorStmt is stored in utilityStmt.

 *

 *      Planning converts a Query tree into a Plan tree headed by a PlannedStmt

 *      node --- the Query structure is not used by the executor.

 */

typedef struct Query

{

    NodeTag        type;



    CmdType        commandType;    /* select|insert|update|delete|utility */



    QuerySource querySource;    /* where did I come from? */



    uint32        queryId;        /* query identifier (can be set by plugins) */



    bool        canSetTag;        /* do I set the command result tag? */



    Node       *utilityStmt;    /* non-null if this is DECLARE CURSOR or a

                                 * non-optimizable statement */



    int            resultRelation; /* rtable index of target relation for

                                 * INSERT/UPDATE/DELETE; 0 for SELECT */



    bool        hasAggs;        /* has aggregates in tlist or havingQual */

    bool        hasWindowFuncs; /* has window functions in tlist */

    bool        hasSubLinks;    /* has subquery SubLink */

    bool        hasDistinctOn;    /* distinctClause is from DISTINCT ON */

    bool        hasRecursive;    /* WITH RECURSIVE was specified */

    bool        hasModifyingCTE;    /* has INSERT/UPDATE/DELETE in WITH */

    bool        hasForUpdate;    /* FOR UPDATE or FOR SHARE was specified */



    List       *cteList;        /* WITH list (of CommonTableExpr's) */



    List       *rtable;            /* list of range table entries */

    FromExpr   *jointree;        /* table join tree (FROM and WHERE clauses) */



    List       *targetList;        /* target list (of TargetEntry) */



    List       *returningList;    /* return-values list (of TargetEntry) */



    List       *groupClause;    /* a list of SortGroupClause's */



    Node       *havingQual;        /* qualifications applied to groups */



    List       *windowClause;    /* a list of WindowClause's */



    List       *distinctClause; /* a list of SortGroupClause's */



    List       *sortClause;        /* a list of SortGroupClause's */



    Node       *limitOffset;    /* # of result tuples to skip (int8 expr) */

    Node       *limitCount;        /* # of result tuples to return (int8 expr) */



    List       *rowMarks;        /* a list of RowMarkClause's */



    Node       *setOperations;    /* set-operation tree if this is top level of

                                 * a UNION/INTERSECT/EXCEPT query */



    List       *constraintDeps; /* a list of pg_constraint OIDs that the query

                                 * depends on to be semantically valid */

} Query;

一点一点地分析吧:

    if (parse->utilityStmt &&

        IsA(parse->utilityStmt, DeclareCursorStmt))

        cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;

parse->utilityStmt 是false,所以不成立。

再接着:

/*

     * Set up global state for this planner invocation.  This data is needed

     * across all levels of sub-Query that might exist in the given command,

     * so we keep it in a separate struct that's linked to by each per-Query

     * PlannerInfo.

     */

    glob = makeNode(PlannerGlobal);



    glob->boundParams = boundParams;

    glob->subplans = NIL;

    glob->subroots = NIL;

    glob->rewindPlanIDs = NULL;

    glob->finalrtable = NIL;

    glob->finalrowmarks = NIL;

    glob->resultRelations = NIL;

    glob->relationOids = NIL;

    glob->invalItems = NIL;

    glob->nParamExec = 0;

    glob->lastPHId = 0;

    glob->lastRowMarkId = 0;

    glob->transientPlan = false;

这一段只是设置了一个初始化好的 PlannerGlobal 指针。

#define newNode(size, tag) \

( \

    AssertMacro((size) >= sizeof(Node)),        /* need the tag, at least */ \

    newNodeMacroHolder = (Node *) palloc0fast(size), \

    newNodeMacroHolder->type = (tag), \

    newNodeMacroHolder \

)

#endif   /* __GNUC__ */





#define makeNode(_type_)        ((_type_ *) newNode(sizeof(_type_),T_##_type_))

接下来,对于我的SQL : select id, val from tst04 where id>1 ,

cursorOPtion 是false,所以不成立,变成:

if (cursorOptions & CURSOR_OPT_FAST_PLAN)

    {

       ...

    }

    else

    {

        /* Default assumption is we need all the tuples */

        tuple_fraction = 0.0;

    }

接下来:

    /* primary planning entry point (may recurse for subqueries) */

    top_plan = subquery_planner(glob, parse, NULL,

                                false, tuple_fraction, &root);

 

 

你可能感兴趣的:(PostgreSQL)