溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL中query_planner函數(shù)處理邏輯是怎樣的

發(fā)布時間:2021-11-11 10:58:21 來源:億速云 閱讀:193 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫

這篇文章主要講解了“PostgreSQL中query_planner函數(shù)處理邏輯是怎樣的”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“PostgreSQL中query_planner函數(shù)處理邏輯是怎樣的”吧!

一、重要的數(shù)據(jù)結(jié)構(gòu)

在query_planner中,對root(PlannerInfo)結(jié)構(gòu)進(jìn)行初始化和處理,為后續(xù)的計劃作準(zhǔn)備.
PlannerInfo

 /*----------
  * PlannerInfo
  *      Per-query information for planning/optimization
  *
  * This struct is conventionally called "root" in all the planner routines.
  * It holds links to all of the planner's working state, in addition to the
  * original Query.  Note that at present the planner extensively modifies
  * the passed-in Query data structure; someday that should stop.
  *----------
  */
 struct AppendRelInfo;
 
 typedef struct PlannerInfo
 {
     NodeTag     type;//Node標(biāo)識
 
     Query      *parse;          /* 查詢樹,the Query being planned */
 
     PlannerGlobal *glob;        /* 當(dāng)前的planner全局信息,global info for current planner run */
 
     Index       query_level;    /* 查詢層次,1標(biāo)識最高層,1 at the outermost Query */
 
     struct PlannerInfo *parent_root;    /* 如為子計劃,則這里存儲父計劃器指針,NULL標(biāo)識最高層,NULL at outermost Query */
 
     /*
      * plan_params contains the expressions that this query level needs to
      * make available to a lower query level that is currently being planned.
      * outer_params contains the paramIds of PARAM_EXEC Params that outer
      * query levels will make available to this query level.
      */
     List       *plan_params;    /* list of PlannerParamItems, see below */
     Bitmapset  *outer_params;
 
     /*
      * simple_rel_array holds pointers to "base rels" and "other rels" (see
      * comments for RelOptInfo for more info).  It is indexed by rangetable
      * index (so entry 0 is always wasted).  Entries can be NULL when an RTE
      * does not correspond to a base relation, such as a join RTE or an
      * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.
      */
     /* RelOptInfo數(shù)組,存儲"base rels",比如基表/子查詢等.該數(shù)組與RTE的順序一一對應(yīng),而且是從1開始,因此[0]無用 */
     struct RelOptInfo **simple_rel_array;   /* All 1-rel RelOptInfos */
     int         simple_rel_array_size;  /* 數(shù)組大小,allocated size of array */
 
     /*
      * simple_rte_array is the same length as simple_rel_array and holds
      * pointers to the associated rangetable entries.  This lets us avoid
      * rt_fetch(), which can be a bit slow once large inheritance sets have
      * been expanded.
      */
     RangeTblEntry **simple_rte_array;   /* RTE數(shù)組,rangetable as an array */
 
     /*
      * append_rel_array is the same length as the above arrays, and holds
      * pointers to the corresponding AppendRelInfo entry indexed by
      * child_relid, or NULL if none.  The array itself is not allocated if
      * append_rel_list is empty.
      */
     struct AppendRelInfo **append_rel_array;//先前已介紹,在處理集合操作如UNION ALL時使用
 
     /*
      * all_baserels is a Relids set of all base relids (but not "other"
      * relids) in the query; that is, the Relids identifier of the final join
      * we need to form.  This is computed in make_one_rel, just before we
      * start making Paths.
      */
     Relids      all_baserels;//"base rels"
 
     /*
      * nullable_baserels is a Relids set of base relids that are nullable by
      * some outer join in the jointree; these are rels that are potentially
      * nullable below the WHERE clause, SELECT targetlist, etc.  This is
      * computed in deconstruct_jointree.
      */
     Relids      nullable_baserels;//Nullable-side端的"base rels"
 
     /*
      * join_rel_list is a list of all join-relation RelOptInfos we have
      * considered in this planning run.  For small problems we just scan the
      * list to do lookups, but when there are many join relations we build a
      * hash table for faster lookups.  The hash table is present and valid
      * when join_rel_hash is not NULL.  Note that we still maintain the list
      * even when using the hash table for lookups; this simplifies life for
      * GEQO.
      */
     List       *join_rel_list;  /* 參與連接的Relation的RelOptInfo鏈表,list of join-relation RelOptInfos */
     struct HTAB *join_rel_hash; /* 可加快鏈表訪問的hash表,optional hashtable for join relations */
 
     /*
      * When doing a dynamic-programming-style join search, join_rel_level[k]
      * is a list of all join-relation RelOptInfos of level k, and
      * join_cur_level is the current level.  New join-relation RelOptInfos are
      * automatically added to the join_rel_level[join_cur_level] list.
      * join_rel_level is NULL if not in use.
      */
     List      **join_rel_level; /* RelOptInfo指針鏈表數(shù)組,k層的join存儲在[k]中,lists of join-relation RelOptInfos */
     int         join_cur_level; /* 當(dāng)前的join層次,index of list being extended */
 
     List       *init_plans;     /* 查詢的初始化計劃鏈表,init SubPlans for query */
 
     List       *cte_plan_ids;   /* CTE子計劃ID鏈表,per-CTE-item list of subplan IDs */
 
     List       *multiexpr_params;   /* List of Lists of Params for MULTIEXPR
                                      * subquery outputs */
 
     List       *eq_classes;     /* 活動的等價類鏈表,list of active EquivalenceClasses */
 
     List       *canon_pathkeys; /* 規(guī)范化PathKey鏈表,list of "canonical" PathKeys */
 
     List       *left_join_clauses;  /* 外連接約束條件鏈表(左),list of RestrictInfos for mergejoinable
                                      * outer join clauses w/nonnullable var on
                                      * left */
 
     List       *right_join_clauses; /* 外連接約束條件鏈表(右),list of RestrictInfos for mergejoinable
                                      * outer join clauses w/nonnullable var on
                                      * right */
 
     List       *full_join_clauses;  /* 全連接約束條件鏈表,list of RestrictInfos for mergejoinable
                                      * full join clauses */
 
     List       *join_info_list; /* 特殊連接信息鏈表,list of SpecialJoinInfos */
 
     List       *append_rel_list;    /* AppendRelInfo鏈表,list of AppendRelInfos */
 
     List       *rowMarks;       /* list of PlanRowMarks */
 
     List       *placeholder_list;   /* PHI鏈表,list of PlaceHolderInfos */
 
     List       *fkey_list;      /* 外鍵信息鏈表,list of ForeignKeyOptInfos */
 
     List       *query_pathkeys; /* uery_planner()要求的PathKeys,desired pathkeys for query_planner() */
 
     List       *group_pathkeys; /* groupClause pathkeys, if any */
     List       *window_pathkeys;    /* pathkeys of bottom window, if any */
     List       *distinct_pathkeys;  /* distinctClause pathkeys, if any */
     List       *sort_pathkeys;  /* sortClause pathkeys, if any */
 
     List       *part_schemes;   /* 已規(guī)范化的分區(qū)Schema,Canonicalised partition schemes used in the
                                  * query. */
 
     List       *initial_rels;   /* 嘗試連接的RelOptInfo鏈表,RelOptInfos we are now trying to join */
 
     /* Use fetch_upper_rel() to get any particular upper rel */
     List       *upper_rels[UPPERREL_FINAL + 1]; /* 上層的RelOptInfo鏈表, upper-rel RelOptInfos */
 
     /* Result tlists chosen by grouping_planner for upper-stage processing */
     struct PathTarget *upper_targets[UPPERREL_FINAL + 1];//
 
     /*
      * grouping_planner passes back its final processed targetlist here, for
      * use in relabeling the topmost tlist of the finished Plan.
      */
     List       *processed_tlist;//最后需處理的投影列
 
     /* Fields filled during create_plan() for use in setrefs.c */
     AttrNumber *grouping_map;   /* for GroupingFunc fixup */
     List       *minmax_aggs;    /* List of MinMaxAggInfos */
 
     MemoryContext planner_cxt;  /* 內(nèi)存上下文,context holding PlannerInfo */
 
     double      total_table_pages;  /* 所有的pages,# of pages in all tables of query */
 
     double      tuple_fraction; /* query_planner輸入?yún)?shù):元組處理比例,tuple_fraction passed to query_planner */
     double      limit_tuples;   /* query_planner輸入?yún)?shù):limit_tuples passed to query_planner */
 
     Index       qual_security_level;    /* 表達(dá)式的最新安全等級,minimum security_level for quals */
     /* Note: qual_security_level is zero if there are no securityQuals */
 
     InheritanceKind inhTargetKind;  /* indicates if the target relation is an
                                      * inheritance child or partition or a
                                      * partitioned table */
     bool        hasJoinRTEs;    /* 存在RTE_JOIN的RTE,true if any RTEs are RTE_JOIN kind */
     bool        hasLateralRTEs; /* 存在標(biāo)記為LATERAL的RTE,true if any RTEs are marked LATERAL */
     bool        hasDeletedRTEs; /* 存在已在jointree刪除的RTE,true if any RTE was deleted from jointree */
     bool        hasHavingQual;  /* 存在Having子句,true if havingQual was non-null */
     bool        hasPseudoConstantQuals; /* true if any RestrictInfo has
                                          * pseudoconstant = true */
     bool        hasRecursion;   /* 遞歸語句,true if planning a recursive WITH item */
 
     /* These fields are used only when hasRecursion is true: */
     int         wt_param_id;    /* PARAM_EXEC ID for the work table */
     struct Path *non_recursive_path;    /* a path for non-recursive term */
 
     /* These fields are workspace for createplan.c */
     Relids      curOuterRels;   /* outer rels above current node */
     List       *curOuterParams; /* not-yet-assigned NestLoopParams */
 
     /* optional private data for join_search_hook, e.g., GEQO */
     void       *join_search_private;
 
     /* Does this query modify any partition key columns? */
     bool        partColsUpdated;
 } PlannerInfo;

二、源碼解讀

本節(jié)介紹query_planner的主流程以及setup_simple_rel_arrays和setup_append_rel_array兩個子函數(shù)的實(shí)現(xiàn)邏輯.
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.  Instead, it
  * returns the RelOptInfo for the top level of joining, and the caller
  * (grouping_planner) can choose among the surviving paths for the rel.
  *
  * query_planner不會處理頂層的處理過程(如最后的分組/排序等操作),因此,不能選擇最優(yōu)的訪問路徑
  * 該函數(shù)會返回RelOptInfo給最高層的連接,grouping_planner可以在剩下的路徑中進(jìn)行選擇
  *
  * root describes the query to plan
  * tlist is the target list the query should produce
  *      (this is NOT necessarily root->parse->targetList!)
  * qp_callback is a function to compute query_pathkeys once it's safe to do so
  * qp_extra is optional extra data to pass to qp_callback
  *
  * root是計劃信息/tlist是投影列
  * qp_callback是計算query_pathkeys的函數(shù)/qp_extra是傳遞給qp_callback的函數(shù)
  *
  * Note: the PlannerInfo node also includes a query_pathkeys field, which
  * tells query_planner the sort order that is desired in the final output
  * plan.  This value is *not* available at call time, but is computed by
  * qp_callback once we have completed merging the query's equivalence classes.
  * (We cannot construct canonical pathkeys until that's done.)
  */
 RelOptInfo *
 query_planner(PlannerInfo *root, List *tlist,
               query_pathkeys_callback qp_callback, void *qp_extra)
 {
     Query      *parse = root->parse;//查詢樹
     List       *joinlist;
     RelOptInfo *final_rel;//結(jié)果
     Index       rti;//RTE的index
     double      total_pages;//總pages數(shù)
 
     /*
      * 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)//簡單SQL,無FROM/WHERE語句
     {
         /* We need a dummy joinrel to describe the empty set of baserels */
         final_rel = build_empty_join_rel(root);//創(chuàng)建返回結(jié)果
 
         /*
          * If query allows parallelism in general, check whether the quals are
          * parallel-restricted.  (We need not check final_rel->reltarget
          * because it's empty at this point.  Anything parallel-restricted in
          * the query tlist will be dealt with later.)
          */
         if (root->glob->parallelModeOK)//并行模式?
             final_rel->consider_parallel =
                 is_parallel_safe(root, parse->jointree->quals);
 
         /* The only path for it is a trivial Result path */
         add_path(final_rel, (Path *)
                  create_result_path(root, final_rel,
                                     final_rel->reltarget,
                                     (List *) parse->jointree->quals));//添加訪問路徑
 
         /* Select cheapest path (pretty easy in this case...) */
         set_cheapest(final_rel);//選擇最優(yōu)的訪問路徑
 
         /*
          * We still are required to call qp_callback, in case it's something
          * like "SELECT 2+2 ORDER BY 1".
          */
         root->canon_pathkeys = NIL;
         (*qp_callback) (root, qp_extra);//回調(diào)函數(shù)
 
         return final_rel;//返回
     }
 
 
     /*
      * Init planner lists to empty.
      *
      * NOTE: append_rel_list was set up by subquery_planner, so do not touch
      * here.
      */
     root->join_rel_list = NIL;//初始化PlannerInfo
     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->fkey_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);//初始化PlannerInfo->simple_rel/rte_array&size
 
     /*
      * Populate append_rel_array with each AppendRelInfo to allow direct
      * lookups by child relid.
      */
     setup_append_rel_array(root);//初始化PlannerInfo->append_rel_array(通過append_rel_list)
 
     /*
      * 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);//構(gòu)建RelOptInfo節(jié)點(diǎn)
 
     /*
      * 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);//構(gòu)建"base rels"的投影列
 
     find_placeholders_in_jointree(root);//處理jointree中的PHI
 
     find_lateral_references(root);//處理jointree中Lateral依賴
 
     joinlist = deconstruct_jointree(root);//重構(gòu)jointree
 
     /*
      * 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);//已創(chuàng)建等價類,那么需要重新考慮被推后處理的外連接表達(dá)式
 
     /*
      * 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);//等價類構(gòu)建后,生成因此外加的約束語句
 
     /*
      * We have completed merging equivalence sets, so it's now possible to
      * generate pathkeys in canonical form; so compute query_pathkeys and
      * other pathkeys fields in PlannerInfo.
      */
     (*qp_callback) (root, qp_extra);//調(diào)用回調(diào)函數(shù)
 
     /*
      * 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);//檢查在子查詢上拉時生成的PH表達(dá)式,確保Vars是OK的
 
     /*
      * 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);//清除無用的外連接
 
     /*
      * Also, reduce any semijoins with unique inner rels to plain inner joins.
      * Likewise, this can't be done until now for lack of needed info.
      */
     reduce_unique_semijoins(root);//消除半連接
 
     /*
      * Now distribute "placeholders" to base rels as needed.  This has to be
      * done after join removal because removal could change whether a
      * placeholder is evaluable at a base rel.
      */
     add_placeholders_to_base_rels(root);//在"base rels"中添加PH
 
     /*
      * Construct the lateral reference sets now that we have finalized
      * PlaceHolderVar eval levels.
      */
     create_lateral_join_info(root);//創(chuàng)建Lateral連接信息
 
     /*
      * Match foreign keys to equivalence classes and join quals.  This must be
      * done after finalizing equivalence classes, and it's useful to wait till
      * after join removal so that we can skip processing foreign keys
      * involving removed relations.
      */
     match_foreign_keys_to_quals(root);//匹配外鍵信息
 
     /*
      * Look for join OR clauses that we can extract single-relation
      * restriction OR clauses from.
      */
     extract_restriction_or_clauses(root);//在OR語句中抽取約束條件
 
     /*
      * 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++)//計算總pages
     {
         RelOptInfo *brel = root->simple_rel_array[rti];
 
         if (brel == NULL)
             continue;
 
         Assert(brel->relid == rti); /* sanity check on array */
 
         if (IS_SIMPLE_REL(brel))
             total_pages += (double) brel->pages;
     }
     root->total_table_pages = total_pages;//賦值
 
     /*
      * Ready to do the primary planning.
      */
     final_rel = make_one_rel(root, joinlist);//執(zhí)行主要的計劃過程
 
     /* Check that we got at least one usable path */
     if (!final_rel || !final_rel->cheapest_total_path ||
         final_rel->cheapest_total_path->param_info != NULL)
         elog(ERROR, "failed to construct the join relation");//檢查
 
     return final_rel;//返回結(jié)果
 }

setup_simple_rel_arrays
初始化setup_simple_rel_arrays(注意:[0]無用)和setup_simple_rel_arrays

 /*
  * setup_simple_rel_arrays
  *    Prepare the arrays we use for quickly accessing base relations.
  */
 void
 setup_simple_rel_arrays(PlannerInfo *root)
 {
     Index       rti;
     ListCell   *lc;
 
     /* Arrays are accessed using RT indexes (1..N) */
     root->simple_rel_array_size = list_length(root->parse->rtable) + 1;
 
     /* simple_rel_array is initialized to all NULLs */
     root->simple_rel_array = (RelOptInfo **)
         palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *));
 
     /* simple_rte_array is an array equivalent of the rtable list */
     root->simple_rte_array = (RangeTblEntry **)
         palloc0(root->simple_rel_array_size * sizeof(RangeTblEntry *));
     rti = 1;
     foreach(lc, root->parse->rtable)
     {
         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
 
         root->simple_rte_array[rti++] = rte;
     }
 }

setup_append_rel_array
源碼比較簡單,讀取append_rel_list中的信息初始化append_rel_array

 /*
  * setup_append_rel_array
  *      Populate the append_rel_array to allow direct lookups of
  *      AppendRelInfos by child relid.
  *
  * The array remains unallocated if there are no AppendRelInfos.
  */
 void
 setup_append_rel_array(PlannerInfo *root)
 {
     ListCell   *lc;
     int         size = list_length(root->parse->rtable) + 1;
 
     if (root->append_rel_list == NIL)
     {
         root->append_rel_array = NULL;
         return;
     }
 
     root->append_rel_array = (AppendRelInfo **)
         palloc0(size * sizeof(AppendRelInfo *));
 
     foreach(lc, root->append_rel_list)
     {
         AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
         int         child_relid = appinfo->child_relid;
 
         /* Sanity check */
         Assert(child_relid < size);
 
         if (root->append_rel_array[child_relid])
             elog(ERROR, "child relation already exists");
 
         root->append_rel_array[child_relid] = appinfo;
     }
 }

感謝各位的閱讀,以上就是“PostgreSQL中query_planner函數(shù)處理邏輯是怎樣的”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對PostgreSQL中query_planner函數(shù)處理邏輯是怎樣的這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI