您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中make_rel_from_joinlist函數分析”,在日常操作中,相信很多人在PostgreSQL中make_rel_from_joinlist函數分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中make_rel_from_joinlist函數分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
make_rel_from_joinlist函數根據連接關系鏈表(joinlist)通過外部算法(鉤子函數)/遺傳算法/動態規劃算法構建連接路徑,其中joinlist鏈表在主函數中已通過調用deconstruct_jointree函數生成.
動態規劃算法的實現standard_join_search函數以及遺傳算法在后續章節再行介紹.
/* * make_rel_from_joinlist * Build access paths using a "joinlist" to guide the join path search. * 依據deconstruct_jointree函數構造的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. * 計算joinlist鏈表中節點的個數。 * 確定使用的算法(動態規劃算法 vs 遺傳算法),如個數<閾值,則考慮所有連接的方式。 */ levels_needed = list_length(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. * 構造與joinlist中元素相對應的rels鏈表。 * 這可能包括base rels和通過子連接構造的base rels。 */ 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);//根據編號找到相應的RelOptInfo } else if (IsA(jlnode, List))//鏈表 { /* Recurse to handle subproblem */ thisrel = make_rel_from_joinlist(root, (List *) jlnode);//遞歸調用,形成新的base rel } else//其他類型,出錯 { elog(ERROR, "unrecognized joinlist node type: %d", (int) nodeTag(jlnode)); thisrel = NULL; /* keep compiler quiet */ } initial_rels = lappend(initial_rels, thisrel);//添加到base rel鏈表中 } if (levels_needed == 1)//連接鏈表只有1個元素 { /* * Single joinlist node, so we're done. */ return (RelOptInfo *) linitial(initial_rels);//直接返回 } else//>1個元素 { /* * Consider the different orders in which we could join the rels, * using a plugin, GEQO, or the regular join search code. * 考慮不同的連接順序->使用外部算法/GEQO遺傳算法/動態規劃算法。 * * 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);//動態規劃算法 } } //----------------------------------------------------------------------- standard_join_search /* * standard_join_search * Find possible joinpaths for a query by successively finding ways * to join component relations into join relations. * 通過動態規劃算法為查詢語句構造連接路徑. * * 'levels_needed' is the number of iterations needed, ie, the number of * independent jointree items in the query. This is > 1. * levels_needed-連接鏈表中的節點個數,>1 * * 'initial_rels' is a list of RelOptInfo nodes for each independent * jointree item. These are the components to be joined together. * Note that levels_needed == list_length(initial_rels). * initial_rels-與連接樹每個元素相對應的RelOptInfo節點 * * Returns the final level of join relations, i.e., the relation that is * the result of joining all the original relations together. * At least one implementation path must be provided for this relation and * all required sub-relations. * 返回連接的最終關系(最頂層的Relation):將所有原始關系連接在一起的最終結果。 * 優化器為該關系及其所必需的子關系提供至少一個的實現路徑。 * * To support loadable plugins that modify planner behavior by changing the * join searching algorithm, we provide a hook variable that lets a plugin * replace or supplement this function. Any such hook must return the same * final join relation as the standard code would, but it might have a * different set of implementation paths attached, and only the sub-joinrels * needed for these paths need have been instantiated. * 為了支持自定義函數,PG提供了一個鉤子變量,允許外部插件替換或填充這個函數。 * 任何這樣的鉤子都必須返回與PG標準函數相同的最終連接關系, * 但是它可能附加了一組不同的實現路徑,并且只實例化了這些路徑所需的子連接。 * * Note to plugin authors: the functions invoked during standard_join_search() * modify root->join_rel_list and root->join_rel_hash. If you want to do more * than one join-order search, you'll probably need to save and restore the * original states of those data structures. See geqo_eval() for an example. */ RelOptInfo * standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels) { int lev; RelOptInfo *rel; /* * This function cannot be invoked recursively within any one planning * problem, so join_rel_level[] can't be in use already. */ Assert(root->join_rel_level == NULL);//驗證 /* * We employ a simple "dynamic programming" algorithm: we first find all * ways to build joins of two jointree items, then all ways to build joins * of three items (from two-item joins and single items), then four-item * joins, and so on until we have considered all ways to join all the * items into one rel. * PG實現了一種簡單的動態規劃算法:首先為連接樹中的兩個Relation建立可能的連接路徑 * 然后為三個Relation建立所有可能的連接路徑,以此類推直至已為所有的Relation建立了 * 連接路徑,從而得到最終的關系(final_rel) * * root->join_rel_level[j] is a list of all the j-item rels. Initially we * set root->join_rel_level[1] to represent all the single-jointree-item * relations. * 設置root->join_rel_level數組,[j]是所有j-item rels的鏈表(即1個item的放在[1]中) */ root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *)); root->join_rel_level[1] = initial_rels;//1個item對應的rel鏈表 for (lev = 2; lev <= levels_needed; lev++)//構造2->N個item對應的rel鏈表 { ListCell *lc; /* * Determine all possible pairs of relations to be joined at this * level, and build paths for making each one from every available * pair of lower-level relations. * 確定在此級別上要連接的所有可能的關系對,并構建訪問路徑, * 以從每一對可用的較低級關系中往上創建關系。 */ join_search_one_level(root, lev); /* * Run generate_partitionwise_join_paths() and generate_gather_paths() * for each just-processed joinrel. We could not do this earlier * because both regular and partial paths can get added to a * particular joinrel at multiple times within join_search_one_level. * 循環調用generate_partitionwise_join_paths()和generate_collect _paths()函數: * 參數為上一步驟生成的鏈表中的每個元素。 * 由于常規路徑和部分路徑都可以在join_search_one_level中多次添加joinrel,因此在此處調用。 * * After that, we're done creating paths for the joinrel, so run * set_cheapest(). * 在此之后,PG已為joinrel(連接生成的新關系)創建了訪問路徑,因此可以調用函數set_cheapest * */ foreach(lc, root->join_rel_level[lev])//遍歷鏈表 { rel = (RelOptInfo *) lfirst(lc);//新生成的關系 /* Create paths for partitionwise joins. */ generate_partitionwise_join_paths(root, rel);//創建partitionwise路徑 /* * Except for the topmost scan/join rel, consider gathering * partial paths. We'll do the same for the topmost scan/join rel * once we know the final targetlist (see grouping_planner). */ if (lev < levels_needed) generate_gather_paths(root, rel, false);//并行執行需考慮gathering /* Find and save the cheapest paths for this rel */ set_cheapest(rel);//從形成該joinrel的所有路徑中找到成本最低的 #ifdef OPTIMIZER_DEBUG debug_print_rel(root, rel);//DEBUG信息 #endif } } /* * We should have a single rel at the final level. * 連接的最終結果,只有一個RelOptInfo */ if (root->join_rel_level[levels_needed] == NIL) elog(ERROR, "failed to build any %d-way joins", levels_needed); Assert(list_length(root->join_rel_level[levels_needed]) == 1); rel = (RelOptInfo *) linitial(root->join_rel_level[levels_needed]);//獲取最終結果 root->join_rel_level = NULL;//重置 return rel;//返回 } //----------------------------------------------------------------------- geqo /* * geqo * solution of the query optimization problem * similar to a constrained Traveling Salesman Problem (TSP) * 遺傳算法:可參考TSP的求解算法. * TSP-旅行推銷員問題(最短路徑問題): * 給定一系列城市和每對城市之間的距離,求解訪問每一座城市一次并回到起始城市的最短回路。 */ RelOptInfo * geqo(PlannerInfo *root, int number_of_rels, List *initial_rels) { GeqoPrivateData private; int generation; Chromosome *momma; Chromosome *daddy; Chromosome *kid; Pool *pool; int pool_size, number_generations; #ifdef GEQO_DEBUG int status_interval; #endif Gene *best_tour; RelOptInfo *best_rel; #if defined(ERX) Edge *edge_table; /* list of edges */ int edge_failures = 0; #endif #if defined(CX) || defined(PX) || defined(OX1) || defined(OX2) City *city_table; /* list of cities */ #endif #if defined(CX) int cycle_diffs = 0; int mutations = 0; #endif /* set up private information */ root->join_search_private = (void *) &private; private.initial_rels = initial_rels; /* initialize private number generator */ geqo_set_seed(root, Geqo_seed); /* set GA parameters */ pool_size = gimme_pool_size(number_of_rels); number_generations = gimme_number_generations(pool_size); #ifdef GEQO_DEBUG status_interval = 10; #endif /* allocate genetic pool memory */ pool = alloc_pool(root, pool_size, number_of_rels); /* random initialization of the pool */ random_init_pool(root, pool); /* sort the pool according to cheapest path as fitness */ sort_pool(root, pool); /* we have to do it only one time, since all * kids replace the worst individuals in * future (-> geqo_pool.c:spread_chromo ) */ #ifdef GEQO_DEBUG elog(DEBUG1, "GEQO selected %d pool entries, best %.2f, worst %.2f", pool_size, pool->data[0].worth, pool->data[pool_size - 1].worth); #endif /* allocate chromosome momma and daddy memory */ momma = alloc_chromo(root, pool->string_length); daddy = alloc_chromo(root, pool->string_length); #if defined (ERX) #ifdef GEQO_DEBUG elog(DEBUG2, "using edge recombination crossover [ERX]"); #endif /* allocate edge table memory */ edge_table = alloc_edge_table(root, pool->string_length); #elif defined(PMX) #ifdef GEQO_DEBUG elog(DEBUG2, "using partially matched crossover [PMX]"); #endif /* allocate chromosome kid memory */ kid = alloc_chromo(root, pool->string_length); #elif defined(CX) #ifdef GEQO_DEBUG elog(DEBUG2, "using cycle crossover [CX]"); #endif /* allocate city table memory */ kid = alloc_chromo(root, pool->string_length); city_table = alloc_city_table(root, pool->string_length); #elif defined(PX) #ifdef GEQO_DEBUG elog(DEBUG2, "using position crossover [PX]"); #endif /* allocate city table memory */ kid = alloc_chromo(root, pool->string_length); city_table = alloc_city_table(root, pool->string_length); #elif defined(OX1) #ifdef GEQO_DEBUG elog(DEBUG2, "using order crossover [OX1]"); #endif /* allocate city table memory */ kid = alloc_chromo(root, pool->string_length); city_table = alloc_city_table(root, pool->string_length); #elif defined(OX2) #ifdef GEQO_DEBUG elog(DEBUG2, "using order crossover [OX2]"); #endif /* allocate city table memory */ kid = alloc_chromo(root, pool->string_length); city_table = alloc_city_table(root, pool->string_length); #endif /* my pain main part: */ /* iterative optimization */ for (generation = 0; generation < number_generations; generation++) { /* SELECTION: using linear bias function */ geqo_selection(root, momma, daddy, pool, Geqo_selection_bias); #if defined (ERX) /* EDGE RECOMBINATION CROSSOVER */ gimme_edge_table(root, momma->string, daddy->string, pool->string_length, edge_table); kid = momma; /* are there any edge failures ? */ edge_failures += gimme_tour(root, edge_table, kid->string, pool->string_length); #elif defined(PMX) /* PARTIALLY MATCHED CROSSOVER */ pmx(root, momma->string, daddy->string, kid->string, pool->string_length); #elif defined(CX) /* CYCLE CROSSOVER */ cycle_diffs = cx(root, momma->string, daddy->string, kid->string, pool->string_length, city_table); /* mutate the child */ if (cycle_diffs == 0) { mutations++; geqo_mutation(root, kid->string, pool->string_length); } #elif defined(PX) /* POSITION CROSSOVER */ px(root, momma->string, daddy->string, kid->string, pool->string_length, city_table); #elif defined(OX1) /* ORDER CROSSOVER */ ox1(root, momma->string, daddy->string, kid->string, pool->string_length, city_table); #elif defined(OX2) /* ORDER CROSSOVER */ ox2(root, momma->string, daddy->string, kid->string, pool->string_length, city_table); #endif /* EVALUATE FITNESS */ kid->worth = geqo_eval(root, kid->string, pool->string_length); /* push the kid into the wilderness of life according to its worth */ spread_chromo(root, kid, pool); #ifdef GEQO_DEBUG if (status_interval && !(generation % status_interval)) print_gen(stdout, pool, generation); #endif } #if defined(ERX) && defined(GEQO_DEBUG) if (edge_failures != 0) elog(LOG, "[GEQO] failures: %d, average: %d", edge_failures, (int) number_generations / edge_failures); else elog(LOG, "[GEQO] no edge failures detected"); #endif #if defined(CX) && defined(GEQO_DEBUG) if (mutations != 0) elog(LOG, "[GEQO] mutations: %d, generations: %d", mutations, number_generations); else elog(LOG, "[GEQO] no mutations processed"); #endif #ifdef GEQO_DEBUG print_pool(stdout, pool, 0, pool_size - 1); #endif #ifdef GEQO_DEBUG elog(DEBUG1, "GEQO best is %.2f after %d generations", pool->data[0].worth, number_generations); #endif /* * got the cheapest query tree processed by geqo; first element of the * population indicates the best query tree */ best_tour = (Gene *) pool->data[0].string; best_rel = gimme_tree(root, best_tour, pool->string_length); if (best_rel == NULL) elog(ERROR, "geqo failed to make a valid plan"); /* DBG: show the query plan */ #ifdef NOT_USED print_plan(best_plan, root); #endif /* ... free memory stuff */ free_chromo(root, momma); free_chromo(root, daddy); #if defined (ERX) free_edge_table(root, edge_table); #elif defined(PMX) free_chromo(root, kid); #elif defined(CX) free_chromo(root, kid); free_city_table(root, city_table); #elif defined(PX) free_chromo(root, kid); free_city_table(root, city_table); #elif defined(OX1) free_chromo(root, kid); free_city_table(root, city_table); #elif defined(OX2) free_chromo(root, kid); free_city_table(root, city_table); #endif free_pool(root, pool); /* ... clear root pointer to our private storage */ root->join_search_private = NULL; return best_rel; }
測試腳本以及執行計劃如下:
testdb=# explain verbose select a.*,b.grbh,b.je testdb-# from t_dwxx a, testdb-# lateral (select t1.dwbh,t1.grbh,t2.je testdb(# from t_grxx t1 testdb(# inner join t_jfxx t2 on t1.dwbh = a.dwbh and t1.grbh = t2.grbh) b testdb-# where a.dwbh = '1001' testdb-# order by b.dwbh; QUERY PLAN ------------------------------------------------------------------------------------------------------ Nested Loop (cost=0.87..111.89 rows=10 width=37) Output: a.dwmc, a.dwbh, a.dwdz, t1.grbh, t2.je, t1.dwbh -> Nested Loop (cost=0.58..28.69 rows=10 width=29) Output: a.dwmc, a.dwbh, a.dwdz, t1.grbh, t1.dwbh -> Index Scan using t_dwxx_pkey on public.t_dwxx a (cost=0.29..8.30 rows=1 width=20) Output: a.dwmc, a.dwbh, a.dwdz Index Cond: ((a.dwbh)::text = '1001'::text) -> Index Scan using idx_t_grxx_dwbh on public.t_grxx t1 (cost=0.29..20.29 rows=10 width=9) Output: t1.dwbh, t1.grbh, t1.xm, t1.xb, t1.nl Index Cond: ((t1.dwbh)::text = '1001'::text) -> Index Scan using idx_t_jfxx_grbh on public.t_jfxx t2 (cost=0.29..8.31 rows=1 width=13) Output: t2.grbh, t2.ny, t2.je Index Cond: ((t2.grbh)::text = (t1.grbh)::text)
啟動gdb跟蹤
(gdb) b make_rel_from_joinlist Breakpoint 1 at 0x73f0d3: file allpaths.c, line 2617. (gdb) c Continuing. Breakpoint 1, make_rel_from_joinlist (root=0x176c750, joinlist=0x179e480) at allpaths.c:2617 2617 levels_needed = list_length(joinlist);
進入make_rel_from_joinlist函數,查看joinlist,鏈表中的Node為RangeTblRef,rindex分別是1/3/4
(gdb) p *joinlist $1 = {type = T_List, length = 3, head = 0x17a0448, tail = 0x17a0408} (gdb) p *(Node *)joinlist->head->data.ptr_value $2 = {type = T_RangeTblRef} (gdb) p *(RangeTblRef *)joinlist->head->data.ptr_value $3 = {type = T_RangeTblRef, rtindex = 1} (gdb) p *(RangeTblRef *)joinlist->head->next->data.ptr_value $4 = {type = T_RangeTblRef, rtindex = 3} (gdb) p *(RangeTblRef *)joinlist->head->next->next->data.ptr_value $5 = {type = T_RangeTblRef, rtindex = 4}
鏈表中的Node個數為3,levels_needed=3
(gdb) n 2619 if (levels_needed <= 0) (gdb) p levels_needed $6 = 3
遍歷鏈表,構造RelOptInfo,添加到initial_rels中
(gdb) 2628 foreach(jl, joinlist) ... (gdb) 2637 thisrel = find_base_rel(root, varno); (gdb) 2651 initial_rels = lappend(initial_rels, thisrel);
完成遍歷后,開始構造連接路徑.
遺傳算法的rels閾值為12(通過GUC參數配置)
2672 if (join_search_hook) (gdb) 2674 else if (enable_geqo && levels_needed >= geqo_threshold) (gdb) 2677 return standard_join_search(root, levels_needed, initial_rels); (gdb) p geqo_threshold $7 = 12
進入函數standard_join_search
(gdb) step standard_join_search (root=0x176c750, levels_needed=3, initial_rels=0x17a6308) at allpaths.c:2733 2733 root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *));
開始構造2->N個item對應的rel鏈表
... (gdb) 2746 join_search_one_level(root, lev); (gdb) n 2757 foreach(lc, root->join_rel_level[lev])
調用函數join_search_one_level,查看root->join_rel_level[j]
(gdb) p *root->join_rel_level[2] $10 = {type = T_List, length = 2, head = 0x17a67a8, tail = 0x17a6ec0}
查看鏈表中的RelOptInfo
(gdb) p *(RelOptInfo *)root->join_rel_level[2]->head->data.ptr_value $12 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x17a65d0, rows = 10, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x17a65e8, pathlist = 0x17a68a8, ppilist = 0x0, partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 0, allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0, baserestrictcost = { startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0, has_eclass_joins = true, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0} (gdb) p *(RelOptInfo *)root->join_rel_level[2]->head->next->data.ptr_value $13 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x17a68d8, rows = 10, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x17a6cd0, pathlist = 0x17a7720, ppilist = 0x0, partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 0, allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0, baserestrictcost = { startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0, has_eclass_joins = true, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
查看RelOptInfo中的relids
通過relids可知,第一個RelOptInfo是1/3號rel連接生成的Relation,第二個RelOptInfo是3/4號rel連接生成的Relation
(gdb) set $roi1=(RelOptInfo *)root->join_rel_level[2]->head->data.ptr_value (gdb) set $roi2=(RelOptInfo *)root->join_rel_level[2]->head->next->data.ptr_value (gdb) p *$roi1->relids $16 = {nwords = 1, words = 0x17a65d4} (gdb) p *$roi1->relids->words $17 = 10 -->2 + 8 --> 1/3 號rel (gdb) p *$roi2->relids->words $18 = 24 -->8 + 16 --> 3/4號rel
查看第一個RelOptInfo中的pathlist,該鏈表有2個Node,類型均為T_NestPath(嵌套連接),總成本分別是28.69和4308.57
(gdb) p *$roi1->pathlist $19 = {type = T_List, length = 2, head = 0x17a6888, tail = 0x17a6a10} (gdb) p *(Node *)$roi1->pathlist->head->data.ptr_value $20 = {type = T_NestPath} (gdb) p *(NestPath *)$roi1->pathlist->head->data.ptr_value $21 = {path = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x17a63c0, pathtarget = 0x17a65e8, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10, startup_cost = 0.57750000000000001, total_cost = 28.688484322533327, pathkeys = 0x0}, jointype = JOIN_INNER, inner_unique = false, outerjoinpath = 0x17a2638, innerjoinpath = 0x17a2908, joinrestrictinfo = 0x0} (gdb) p *(Node *)$roi1->pathlist->head->next->data.ptr_value $22 = {type = T_NestPath} (gdb) p *(NestPath *)$roi1->pathlist->head->next->data.ptr_value $23 = {path = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x17a63c0, pathtarget = 0x17a65e8, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10, startup_cost = 0.57750000000000001, total_cost = 4308.5748727883229, pathkeys = 0x17a3650}, jointype = JOIN_INNER, inner_unique = false, outerjoinpath = 0x17a3190, innerjoinpath = 0x17a68f0, joinrestrictinfo = 0x0}
查看第二個RelOptInfo中的pathlist,只有1個Node,類型為T_NestPath(嵌套連接),總成本為103.49
(gdb) p *$roi2->pathlist $24 = {type = T_List, length = 1, head = 0x17a7700, tail = 0x17a7700} (gdb) p *(Node *)$roi2->pathlist->head->data.ptr_value $27 = {type = T_NestPath} (gdb) p *(NestPath *)$roi2->pathlist->head->data.ptr_value $28 = {path = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x17a6ac0, pathtarget = 0x17a6cd0, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10, startup_cost = 0.58499999999999996, total_cost = 103.48598432253331, pathkeys = 0x0}, jointype = JOIN_INNER, inner_unique = false, outerjoinpath = 0x17a2908, innerjoinpath = 0x17a5470, joinrestrictinfo = 0x0}
通過set_cheapest函數設置成本最低的訪問路徑,結果存儲在cheapest_startup_path和cheapest_total_path中
(gdb) 2773 set_cheapest(rel); (gdb) 2757 foreach(lc, root->join_rel_level[lev]) ... (gdb) p *$roi1 $35 = ..., cheapest_startup_path = 0x17a67f8, cheapest_total_path = 0x17a67f8, ... (gdb) p *$roi2 $36 =..., cheapest_startup_path = 0x17a7750, cheapest_total_path = 0x17a7750, ...
繼續循環,這時候lev=3
(gdb) n 2737 for (lev = 2; lev <= levels_needed; lev++) (gdb) n 2746 join_search_one_level(root, lev); (gdb) p lev $38 = 3
得到3張表連接的final_rel
(gdb) p *root->join_rel_level[3] $41 = {type = T_List, length = 1, head = 0x17a8090, tail = 0x17a8090} (gdb) p *(RelOptInfo *)root->join_rel_level[3]->head->data.ptr_value $42 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x17a74d8, rows = 10, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x17a7e40, pathlist = 0x17a8258, ppilist = 0x0, partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 0, allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0, baserestrictcost = { startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0, has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
查看pathlist,只有1個元素,類型為NestPath,該訪問路徑成本為111.89
(gdb) set $roi=(RelOptInfo *)root->join_rel_level[3]->head->data.ptr_value (gdb) p *$roi->pathlist $44 = {type = T_List, length = 1, head = 0x17a8238, tail = 0x17a8238} (gdb) p *(Node *)$roi->pathlist->head->data.ptr_value $45 = {type = T_NestPath} (gdb) p *(NestPath *)$roi->pathlist->head->data.ptr_value $46 = {path = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x17a7c30, pathtarget = 0x17a7e40, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10, startup_cost = 0.87, total_cost = 111.88848432253332, pathkeys = 0x0}, jointype = JOIN_INNER, inner_unique = false, outerjoinpath = 0x17a67f8, innerjoinpath = 0x17a5470, joinrestrictinfo = 0x0}
獲得最終結果
... 2792 return rel; (gdb) p *rel $47 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x17a74d8, rows = 10, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x17a7e40, pathlist = 0x17a8258, ppilist = 0x0, partial_pathlist = 0x0, cheapest_startup_path = 0x17a8318, cheapest_total_path = 0x17a8318, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x17a89b0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 0, allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0, baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0, has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0} (gdb) p *rel->cheapest_total_path $48 = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x17a7c30, pathtarget = 0x17a7e40, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 10, startup_cost = 0.87, total_cost = 111.88848432253332, pathkeys = 0x0}
到此,關于“PostgreSQL中make_rel_from_joinlist函數分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。