您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中query_planner中主計劃函數make_one_rel的主實現邏輯是什么”,在日常操作中,相信很多人在PostgreSQL中query_planner中主計劃函數make_one_rel的主實現邏輯是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中query_planner中主計劃函數make_one_rel的主實現邏輯是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
query_planner代碼片段:
//... /* * Ready to do the primary planning. */ final_rel = make_one_rel(root, joinlist);//執行主要的計劃過程 /* 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;//返回結果 //...
RelOptInfo
typedef struct RelOptInfo { NodeTag type;//節點標識 RelOptKind reloptkind;//RelOpt類型 /* all relations included in this RelOptInfo */ Relids relids; /*Relids(rtindex)集合 set of base relids (rangetable indexes) */ /* size estimates generated by planner */ double rows; /*結果元組的估算數量 estimated number of result tuples */ /* per-relation planner control flags */ bool consider_startup; /*是否考慮啟動成本?是,需要保留啟動成本低的路徑 keep cheap-startup-cost paths? */ bool consider_param_startup; /*是否考慮參數化?的路徑 ditto, for parameterized paths? */ bool consider_parallel; /*是否考慮并行處理路徑 consider parallel paths? */ /* default result targetlist for Paths scanning this relation */ struct PathTarget *reltarget; /*掃描該Relation時默認的結果 list of Vars/Exprs, cost, width */ /* materialization information */ List *pathlist; /*訪問路徑鏈表 Path structures */ List *ppilist; /*路徑鏈表中使用參數化路徑進行 ParamPathInfos used in pathlist */ List *partial_pathlist; /* partial Paths */ struct Path *cheapest_startup_path;//代價最低的啟動路徑 struct Path *cheapest_total_path;//代價最低的整體路徑 struct Path *cheapest_unique_path;//代價最低的獲取唯一值的路徑 List *cheapest_parameterized_paths;//代價最低的參數化?路徑鏈表 /* parameterization information needed for both base rels and join rels */ /* (see also lateral_vars and lateral_referencers) */ Relids direct_lateral_relids; /*使用lateral語法,需依賴的Relids rels directly laterally referenced */ Relids lateral_relids; /* minimum parameterization of rel */ /* information about a base rel (not set for join rels!) */ //reloptkind=RELOPT_BASEREL時使用的數據結構 Index relid; /* Relation ID */ Oid reltablespace; /* 表空間 containing tablespace */ RTEKind rtekind; /* 基表?子查詢?還是函數等等?RELATION, SUBQUERY, FUNCTION, etc */ AttrNumber min_attr; /* 最小的屬性編號 smallest attrno of rel (often <0) */ AttrNumber max_attr; /* 最大的屬性編號 largest attrno of rel */ Relids *attr_needed; /* 數組 array indexed [min_attr .. max_attr] */ int32 *attr_widths; /* 屬性寬度 array indexed [min_attr .. max_attr] */ List *lateral_vars; /* 關系依賴的Vars/PHVs LATERAL Vars and PHVs referenced by rel */ Relids lateral_referencers; /*依賴該關系的Relids rels that reference me laterally */ List *indexlist; /* 該關系的IndexOptInfo鏈表 list of IndexOptInfo */ List *statlist; /* 統計信息鏈表 list of StatisticExtInfo */ BlockNumber pages; /* 塊數 size estimates derived from pg_class */ double tuples; /* 元組數 */ double allvisfrac; /* ? */ PlannerInfo *subroot; /* 如為子查詢,存儲子查詢的root if subquery */ List *subplan_params; /* 如為子查詢,存儲子查詢的參數 if subquery */ int rel_parallel_workers; /* 并行執行,需要多少個workers? wanted number of parallel workers */ /* Information about foreign tables and foreign joins */ //FWD相關信息 Oid serverid; /* identifies server for the table or join */ Oid userid; /* identifies user to check access as */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_private; /* cache space for remembering if we have proven this relation unique */ //已知的,可保證唯一的Relids鏈表 List *unique_for_rels; /* known unique for these other relid * set(s) */ List *non_unique_for_rels; /* 已知的,不唯一的Relids鏈表 known not unique for these set(s) */ /* used by various scans and joins: */ List *baserestrictinfo; /* 如為基本關系,存儲約束條件 RestrictInfo structures (if base rel) */ QualCost baserestrictcost; /* 解析約束表達式的成本? cost of evaluating the above */ Index baserestrict_min_security; /* 最低安全等級 min security_level found in * baserestrictinfo */ List *joininfo; /* 連接語句的約束條件信息 RestrictInfo structures for join clauses * involving this rel */ bool has_eclass_joins; /* 是否存在等價類連接? T means joininfo is incomplete */ /* used by partitionwise joins: */ bool consider_partitionwise_join; /* 分區? consider partitionwise * join paths? (if * partitioned rel) */ Relids top_parent_relids; /* Relids of topmost parents (if "other" * rel) */ /* used for partitioned relations */ //分區表使用 PartitionScheme part_scheme; /* 分區的schema Partitioning scheme. */ int nparts; /* 分區數 number of partitions */ struct PartitionBoundInfoData *boundinfo; /* 分區邊界信息 Partition bounds */ List *partition_qual; /* 分區約束 partition constraint */ struct RelOptInfo **part_rels; /* 分區的RelOptInfo數組 Array of RelOptInfos of partitions, * stored in the same order of bounds */ List **partexprs; /* 非空分區鍵表達式 Non-nullable partition key expressions. */ List **nullable_partexprs; /* 可為空的分區鍵表達式 Nullable partition key expressions. */ List *partitioned_child_rels; /* RT Indexes鏈表 List of RT indexes. */ } RelOptInfo;
make_one_rel
make_one_rel函數找出執行查詢的所有可能訪問路徑,但不考慮上層的Non-SPJ操作,返回一個最上層的RelOptInfo.
make_one_rel函數分為兩個階段:生成掃描路徑(set_base_rel_pathlists)和生成連接路徑(make_rel_from_joinlist).
注:SPJ是指選擇(Select)/投影(Project)/連接(Join),相對應的Non-SPJ操作是指Group分組/Sort排序等操作
/* * make_one_rel * Finds all possible access paths for executing a query, returning a * single rel that represents the join of all base rels in the query. */ RelOptInfo * make_one_rel(PlannerInfo *root, List *joinlist) { RelOptInfo *rel; Index rti; /* * Construct the all_baserels Relids set. */ root->all_baserels = NULL; for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍歷RelOptInfo { RelOptInfo *brel = root->simple_rel_array[rti]; /* there may be empty slots corresponding to non-baserel RTEs */ if (brel == NULL) continue; Assert(brel->relid == rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ if (brel->reloptkind != RELOPT_BASEREL) continue; root->all_baserels = bms_add_member(root->all_baserels, brel->relid);//添加到all_baserels遍歷中 } /* Mark base rels as to whether we care about fast-start plans */ //設置RelOptInfo的consider_param_startup變量,是否考量fast-start plans set_base_rel_consider_startup(root); /* * Compute size estimates and consider_parallel flags for each base rel, * then generate access paths. */ set_base_rel_sizes(root);//估算Relation的Size并且設置consider_parallel標記 set_base_rel_pathlists(root);//生成Relation的掃描(訪問)路徑 /* * Generate access paths for the entire join tree. * 通過動態規劃或遺傳算法生成連接路徑 */ rel = make_rel_from_joinlist(root, joinlist); /* * The result should join all and only the query's base rels. */ Assert(bms_equal(rel->relids, root->all_baserels)); //返回最上層的RelOptInfo return rel; } //-------------------------------------------------------- /* * set_base_rel_consider_startup * Set the consider_[param_]startup flags for each base-relation entry. * * For the moment, we only deal with consider_param_startup here; because the * logic for consider_startup is pretty trivial and is the same for every base * relation, we just let build_simple_rel() initialize that flag correctly to * start with. If that logic ever gets more complicated it would probably * be better to move it here. */ static void set_base_rel_consider_startup(PlannerInfo *root) { /* * Since parameterized paths can only be used on the inside of a nestloop * join plan, there is usually little value in considering fast-start * plans for them. However, for relations that are on the RHS of a SEMI * or ANTI join, a fast-start plan can be useful because we're only going * to care about fetching one tuple anyway. * * To minimize growth of planning time, we currently restrict this to * cases where the RHS is a single base relation, not a join; there is no * provision for consider_param_startup to get set at all on joinrels. * Also we don't worry about appendrels. costsize.c's costing rules for * nestloop semi/antijoins don't consider such cases either. */ ListCell *lc; foreach(lc, root->join_info_list) { SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); int varno; if ((sjinfo->jointype == JOIN_SEMI || sjinfo->jointype == JOIN_ANTI) && bms_get_singleton_member(sjinfo->syn_righthand, &varno)) { RelOptInfo *rel = find_base_rel(root, varno); rel->consider_param_startup = true; } } } //-------------------------------------------------------- /* * set_base_rel_sizes * Set the size estimates (rows and widths) for each base-relation entry. * Also determine whether to consider parallel paths for base relations. * * We do this in a separate pass over the base rels so that rowcount * estimates are available for parameterized path generation, and also so * that each rel's consider_parallel flag is set correctly before we begin to * generate paths. */ static void set_base_rel_sizes(PlannerInfo *root) { Index rti; for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍歷RelOptInfo數組 { RelOptInfo *rel = root->simple_rel_array[rti]; RangeTblEntry *rte; /* there may be empty slots corresponding to non-baserel RTEs */ if (rel == NULL) continue; Assert(rel->relid == rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ if (rel->reloptkind != RELOPT_BASEREL) continue; rte = root->simple_rte_array[rti]; /* * If parallelism is allowable for this query in general, see whether * it's allowable for this rel in particular. We have to do this * before set_rel_size(), because (a) if this rel is an inheritance * parent, set_append_rel_size() will use and perhaps change the rel's * consider_parallel flag, and (b) for some RTE types, set_rel_size() * goes ahead and makes paths immediately. */ if (root->glob->parallelModeOK) set_rel_consider_parallel(root, rel, rte); set_rel_size(root, rel, rti, rte); } } /* * set_rel_size * Set size estimates for a base relation */ static void set_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { if (rel->reloptkind == RELOPT_BASEREL && relation_excluded_by_constraints(root, rel, rte)) { /* * We proved we don't need to scan the rel via constraint exclusion, * so set up a single dummy path for it. Here we only check this for * regular baserels; if it's an otherrel, CE was already checked in * set_append_rel_size(). * * In this case, we go ahead and set up the relation's path right away * instead of leaving it for set_rel_pathlist to do. This is because * we don't have a convention for marking a rel as dummy except by * assigning a dummy path to it. */ set_dummy_rel_pathlist(rel);// } else if (rte->inh)//inherit table { /* It's an "append relation", process accordingly */ set_append_rel_size(root, rel, rti, rte); } else { switch (rel->rtekind) { case RTE_RELATION://數據表 if (rte->relkind == RELKIND_FOREIGN_TABLE)//FDW { /* Foreign table */ set_foreign_size(root, rel, rte); } else if (rte->relkind == RELKIND_PARTITIONED_TABLE)//分區表 { /* * A partitioned table without any partitions is marked as * a dummy rel. */ set_dummy_rel_pathlist(rel); } else if (rte->tablesample != NULL)//采樣表 { /* Sampled relation */ set_tablesample_rel_size(root, rel, rte); } else { /* Plain relation */ set_plain_rel_size(root, rel, rte);//常規的數據表 } break; case RTE_SUBQUERY://子查詢 /* * Subqueries don't support making a choice between * parameterized and unparameterized paths, so just go ahead * and build their paths immediately. */ set_subquery_pathlist(root, rel, rti, rte);//生成子查詢訪問路徑 break; case RTE_FUNCTION://FUNCTION set_function_size_estimates(root, rel); break; case RTE_TABLEFUNC://TABLEFUNC set_tablefunc_size_estimates(root, rel); break; case RTE_VALUES://VALUES set_values_size_estimates(root, rel); break; case RTE_CTE://CTE /* * CTEs don't support making a choice between parameterized * and unparameterized paths, so just go ahead and build their * paths immediately. */ if (rte->self_reference) set_worktable_pathlist(root, rel, rte); else set_cte_pathlist(root, rel, rte); break; case RTE_NAMEDTUPLESTORE://NAMEDTUPLESTORE,命名元組存儲 set_namedtuplestore_pathlist(root, rel, rte); break; default: elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind); break; } } /* * We insist that all non-dummy rels have a nonzero rowcount estimate. */ Assert(rel->rows > 0 || IS_DUMMY_REL(rel)); } //-------------------------------------------------------- /* * set_base_rel_pathlists * Finds all paths available for scanning each base-relation entry. * Sequential scan and any available indices are considered. * Each useful path is attached to its relation's 'pathlist' field. * * 為每一個base rels找出所有可用的訪問路徑(順序掃描和所有可用的索引都會考慮在內) * 每一個可用的路徑都會添加到pathlist鏈表中 * */ static void set_base_rel_pathlists(PlannerInfo *root) { Index rti; for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍歷RelOptInfo數組 { RelOptInfo *rel = root->simple_rel_array[rti]; /* there may be empty slots corresponding to non-baserel RTEs */ if (rel == NULL) continue; Assert(rel->relid == rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ if (rel->reloptkind != RELOPT_BASEREL) continue; set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti]); } } /* * set_rel_pathlist * Build access paths for a base relation */ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { if (IS_DUMMY_REL(rel)) { /* We already proved the relation empty, so nothing more to do */ } else if (rte->inh)//inherit { /* It's an "append relation", process accordingly */ set_append_rel_pathlist(root, rel, rti, rte); } else//常規 { switch (rel->rtekind) { case RTE_RELATION://數據表 if (rte->relkind == RELKIND_FOREIGN_TABLE)//FDW { /* Foreign table */ set_foreign_pathlist(root, rel, rte); } else if (rte->tablesample != NULL)//采樣表 { /* Sampled relation */ set_tablesample_rel_pathlist(root, rel, rte); } else//常規數據表 { /* Plain relation */ set_plain_rel_pathlist(root, rel, rte); } break; case RTE_SUBQUERY://子查詢 /* Subquery --- 已在set_rel_size處理,fully handled during set_rel_size */ break; case RTE_FUNCTION: /* RangeFunction */ set_function_pathlist(root, rel, rte); break; case RTE_TABLEFUNC: /* Table Function */ set_tablefunc_pathlist(root, rel, rte); break; case RTE_VALUES: /* Values list */ set_values_pathlist(root, rel, rte); break; case RTE_CTE: /* CTE reference --- fully handled during set_rel_size */ break; case RTE_NAMEDTUPLESTORE: /* tuplestore reference --- fully handled during set_rel_size */ break; default: elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind); break; } } /* * If this is a baserel, we should normally consider gathering any partial * paths we may have created for it. * * However, if this is an inheritance child, skip it. Otherwise, we could * end up with a very large number of gather nodes, each trying to grab * its own pool of workers. Instead, we'll consider gathering partial * paths for the parent appendrel. * * Also, if this is the topmost scan/join rel (that is, the only baserel), * we postpone this until the final scan/join targelist is available (see * grouping_planner). */ if (rel->reloptkind == RELOPT_BASEREL && bms_membership(root->all_baserels) != BMS_SINGLETON) generate_gather_paths(root, rel, false); /* * Allow a plugin to editorialize on the set of Paths for this base * relation. It could add new paths (such as CustomPaths) by calling * add_path(), or delete or modify paths added by the core code. */ if (set_rel_pathlist_hook)//鉤子函數 (*set_rel_pathlist_hook) (root, rel, rti, rte); /* Now find the cheapest of the paths for this rel */ set_cheapest(rel);//找出代價最低的訪問路徑 #ifdef OPTIMIZER_DEBUG debug_print_rel(root, rel); #endif } //------------------------------------------------------------ /* * make_rel_from_joinlist * Build access paths using a "joinlist" to guide the join path search. * * 根據joinlist構建連接訪問路徑,joinlist是函數deconstruct_jointree函數的返回 * * See comments for deconstruct_jointree() for definition of the joinlist * data structure. */ static RelOptInfo * make_rel_from_joinlist(PlannerInfo *root, List *joinlist) { int levels_needed; List *initial_rels; ListCell *jl; /* * Count the number of child joinlist nodes. This is the depth of the * dynamic-programming algorithm we must employ to consider all ways of * joining the child nodes. */ levels_needed = list_length(joinlist);//joinlist鏈表長度 if (levels_needed <= 0) return NULL; /* nothing to do? */ /* * Construct a list of rels corresponding to the child joinlist nodes. * This may contain both base rels and rels constructed according to * sub-joinlists. */ initial_rels = NIL; foreach(jl, joinlist)//遍歷鏈表 { Node *jlnode = (Node *) lfirst(jl); RelOptInfo *thisrel; if (IsA(jlnode, RangeTblRef))//RTR { int varno = ((RangeTblRef *) jlnode)->rtindex; thisrel = find_base_rel(root, varno); } else if (IsA(jlnode, List)) { /* Recurse to handle subproblem */ thisrel = make_rel_from_joinlist(root, (List *) jlnode);//遞歸調用 } else { elog(ERROR, "unrecognized joinlist node type: %d", (int) nodeTag(jlnode)); thisrel = NULL; /* keep compiler quiet */ } initial_rels = lappend(initial_rels, thisrel);//加入初始化鏈表中 } if (levels_needed == 1) { /* * Single joinlist node, so we're done. */ return (RelOptInfo *) linitial(initial_rels); } else { /* * Consider the different orders in which we could join the rels, * using a plugin, GEQO, or the regular join search code. * * We put the initial_rels list into a PlannerInfo field because * has_legal_joinclause() needs to look at it (ugly :-(). */ root->initial_rels = initial_rels; if (join_search_hook)//鉤子函數 return (*join_search_hook) (root, levels_needed, initial_rels); else if (enable_geqo && levels_needed >= geqo_threshold) return geqo(root, levels_needed, initial_rels);//通過遺傳算法構建連接訪問路徑 else return standard_join_search(root, levels_needed, initial_rels);//通過動態規劃算法構建連接路徑 } }
到此,關于“PostgreSQL中query_planner中主計劃函數make_one_rel的主實現邏輯是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。