您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中set_base_rel_pathlists函數有什么作用”,在日常操作中,相信很多人在PostgreSQL中set_base_rel_pathlists函數有什么作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中set_base_rel_pathlists函數有什么作用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
set_base_rel_pathlists函數的目的是為每一個base rel找出所有可用的訪問路徑(包括順序掃描和所有可用的索引),每一個可用的路徑都會添加到pathlist鏈表中。
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 */ //FDW相關信息 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; /* 是否存在等價類連接? True意味著joininfo并不完整,,T means joininfo is incomplete */ /* used by partitionwise joins: */ //是否嘗試partitionwise連接,這是PG 11的一個新特性. 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;
Cost相關
注意:實際使用的參數值通過系統配置文件定義,而不是這里的常量定義!
/* * The cost estimate produced by cost_qual_eval() includes both a one-time * (startup) cost, and a per-tuple cost. */ typedef struct QualCost { Cost startup; /* 啟動成本,one-time cost */ Cost per_tuple; /* 每個元組的成本,per-evaluation cost */ } QualCost; typedef double Cost; /* execution cost (in page-access units) */ /* defaults for costsize.c's Cost parameters */ /* NB: cost-estimation code should use the variables, not these constants! */ /* 注意:實際值通過系統配置文件定義,而不是這里的常量定義! */ /* If you change these, update backend/utils/misc/postgresql.sample.conf */ #define DEFAULT_SEQ_PAGE_COST 1.0 //順序掃描page的成本 #define DEFAULT_RANDOM_PAGE_COST 4.0 //隨機掃描page的成本 #define DEFAULT_CPU_TUPLE_COST 0.01 //處理一個元組的CPU成本 #define DEFAULT_CPU_INDEX_TUPLE_COST 0.005 //處理一個索引元組的CPU成本 #define DEFAULT_CPU_OPERATOR_COST 0.0025 //執行一次操作或函數的CPU成本 #define DEFAULT_PARALLEL_TUPLE_COST 0.1 //并行執行,從一個worker傳輸一個元組到另一個worker的成本 #define DEFAULT_PARALLEL_SETUP_COST 1000.0 //構建并行執行環境的成本 #define DEFAULT_EFFECTIVE_CACHE_SIZE 524288 /*先前已有介紹, measured in pages */ double seq_page_cost = DEFAULT_SEQ_PAGE_COST; double random_page_cost = DEFAULT_RANDOM_PAGE_COST; double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST; double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST; double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST; double parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST; double parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST; int effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE; Cost disable_cost = 1.0e10;//1后面10個0,通過設置一個巨大的成本,讓優化器自動放棄此路徑 int max_parallel_workers_per_gather = 2;//每次gather使用的worker數
IndexClauseSet
用于收集匹配索引的的條件語句
/* Data structure for collecting qual clauses that match an index */ typedef struct { bool nonempty; /* True if lists are not all empty */ /* Lists of RestrictInfos, one per index column */ List *indexclauses[INDEX_MAX_KEYS]; } IndexClauseSet;
set_base_rel_pathlists函數遍歷RelOptInfo數組,為每一個Rel構造訪問路徑,先前已介紹了順序掃描的成本估算,本節介紹索引掃描的成本估算(函數:create_index_paths),通過調用set_plain_rel_pathlist->create_index_paths函數實現.
/* * set_plain_rel_pathlist * Build access paths for a plain relation (no subquery, no inheritance) */ static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relids required_outer; //... /* 索引掃描,Consider index scans */ create_index_paths(root, rel); /* TID掃描,Consider TID scans */ create_tidscan_paths(root, rel); }
create_index_paths
create_index_paths函數生成Relation所有可能被選中的索引訪問路徑,詳見源碼注釋.
/* * create_index_paths() * Generate all interesting index paths for the given relation. * Candidate paths are added to the rel's pathlist (using add_path). * 生成Relation所有可能被選中的索引訪問路徑. * Paths通過add_path方法加入到RelOptInfo的pathlist鏈表中. * * To be considered for an index scan, an index must match one or more * restriction clauses or join clauses from the query's qual condition, * or match the query's ORDER BY condition, or have a predicate that * matches the query's qual condition. * 使用索引掃描的前提是:1.索引必須匹配一個或多個限制條件或連接條件,或者 * 2.匹配查詢的ORDER BY排序條件,或者3.匹配查詢條件的謂詞(部分/條件索引) * * There are two basic kinds of index scans. A "plain" index scan uses * only restriction clauses (possibly none at all) in its indexqual, * so it can be applied in any context. A "parameterized" index scan uses * join clauses (plus restriction clauses, if available) in its indexqual. * When joining such a scan to one of the relations supplying the other * variables used in its indexqual, the parameterized scan must appear as * the inner relation of a nestloop join; it can't be used on the outer side, * nor in a merge or hash join. In that context, values for the other rels' * attributes are available and fixed during any one scan of the indexpath. * 有兩種基本的索引掃描類型,一種是"plain"索引掃描,只使用限制條件(或者什么都 * 沒有),這種掃描方法適用于任何場景.另外一種是"parameterized"掃描,使用連接條件 * (可能的話,加上限制條件)."parameterized"掃描只能出現在嵌套循環中的內關系中, * 因為參數由外關系提供. * * An IndexPath is generated and submitted to add_path() for each plain or * parameterized index scan this routine deems potentially interesting for * the current query. * IndexPath訪問路徑通過函數add_path生成并提交. * * 輸入參數: * 'rel' is the relation for which we want to generate index paths * rel是待生成索引范圍路徑的關系 * * Note: check_index_predicates() must have been run previously for this rel. * 注意:函數check_index_predicates在調用此函數前調用 * * Note: in cases involving LATERAL references in the relation's tlist, it's * possible that rel->lateral_relids is nonempty. Currently, we include * lateral_relids into the parameterization reported for each path, but don't * take it into account otherwise. The fact that any such rels *must* be * available as parameter sources perhaps should influence our choices of * index quals ... but for now, it doesn't seem worth troubling over. * In particular, comments below about "unparameterized" paths should be read * as meaning "unparameterized so far as the indexquals are concerned". */ void create_index_paths(PlannerInfo *root, RelOptInfo *rel) { List *indexpaths;//索引訪問路徑鏈表 List *bitindexpaths;// List *bitjoinpaths; List *joinorclauses; IndexClauseSet rclauseset; IndexClauseSet jclauseset; IndexClauseSet eclauseset; ListCell *lc; /* Skip the whole mess if no indexes */ if (rel->indexlist == NIL)//不存在索引,退出 return; /* Bitmap paths are collected and then dealt with at the end */ bitindexpaths = bitjoinpaths = joinorclauses = NIL;//初始賦值 /* Examine each index in turn */ foreach(lc, rel->indexlist)//遍歷索引鏈表 { IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);//索引信息 /* Protect limited-size array in IndexClauseSets */ Assert(index->ncolumns <= INDEX_MAX_KEYS); /* * Ignore partial indexes that do not match the query. * (generate_bitmap_or_paths() might be able to do something with * them, but that's of no concern here.) */ if (index->indpred != NIL && !index->predOK)//部分索引,而且不能使用,不使用此索引 continue; /* * Identify the restriction clauses that can match the index. * 驗證索引和條件是否匹配 */ MemSet(&rclauseset, 0, sizeof(rclauseset)); match_restriction_clauses_to_index(rel, index, &rclauseset); /* * Build index paths from the restriction clauses. These will be * non-parameterized paths. Plain paths go directly to add_path(), * bitmap paths are added to bitindexpaths to be handled below. * 通過限制條件創建非參數化索引訪問路徑.Plain訪問路徑通過函數add_path直接添加到RelOptInfo中 * 位圖訪問路徑添加到bitindexpaths鏈表中,后續再處理 */ get_index_paths(root, rel, index, &rclauseset, &bitindexpaths); /* * Identify the join clauses that can match the index. For the moment * we keep them separate from the restriction clauses. Note that this * step finds only "loose" join clauses that have not been merged into * EquivalenceClasses. Also, collect join OR clauses for later. * 驗證索引是否與連接條件匹配(連接條件與限制條件相互獨立). * 這一步只是發現未被合并到EC中的"loose"連接條件,在此之后會收集連接中的OR條件 */ MemSet(&jclauseset, 0, sizeof(jclauseset)); match_join_clauses_to_index(root, rel, index, &jclauseset, &joinorclauses); /* * Look for EquivalenceClasses that can generate joinclauses matching * the index. * 通過EC(等價類)匹配索引,結果存儲在eclauseset鏈表中 */ MemSet(&eclauseset, 0, sizeof(eclauseset)); match_eclass_clauses_to_index(root, index, &eclauseset); /* * If we found any plain or eclass join clauses, build parameterized * index paths using them. * 如果存在plain或者eclass連接條件,創建參數化索引訪問路徑 */ if (jclauseset.nonempty || eclauseset.nonempty) consider_index_join_clauses(root, rel, index, &rclauseset, &jclauseset, &eclauseset, &bitjoinpaths); } /* * Generate BitmapOrPaths for any suitable OR-clauses present in the * restriction list. Add these to bitindexpaths. * 基于RelOptInfo中的限制條件生成BitmapOrPaths訪問路徑 */ indexpaths = generate_bitmap_or_paths(root, rel, rel->baserestrictinfo, NIL); bitindexpaths = list_concat(bitindexpaths, indexpaths);//合并到bitindexpaths鏈表中 /* * Likewise, generate BitmapOrPaths for any suitable OR-clauses present in * the joinclause list. Add these to bitjoinpaths. * 同樣的,基于連接條件joinorclause中的OR語句生成BitmapOrPaths訪問路徑 */ indexpaths = generate_bitmap_or_paths(root, rel, joinorclauses, rel->baserestrictinfo); bitjoinpaths = list_concat(bitjoinpaths, indexpaths);//合并到bitjoinpaths鏈表中 /* * If we found anything usable, generate a BitmapHeapPath for the most * promising combination of restriction bitmap index paths. Note there * will be only one such path no matter how many indexes exist. This * should be sufficient since there's basically only one figure of merit * (total cost) for such a path. */ if (bitindexpaths != NIL)//存在位圖索引訪問路徑 { Path *bitmapqual;//訪問路徑 BitmapHeapPath *bpath;//BitmapHeapPath訪問路徑 bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);//位圖表達式路徑 bpath = create_bitmap_heap_path(root, rel, bitmapqual, rel->lateral_relids, 1.0, 0);//BitmapHeapPath訪問路徑 add_path(rel, (Path *) bpath);//添加到RelOptInfo中 /* create a partial bitmap heap path */ if (rel->consider_parallel && rel->lateral_relids == NULL) create_partial_bitmap_paths(root, rel, bitmapqual);//創建并行訪問路徑 } /* * Likewise, if we found anything usable, generate BitmapHeapPaths for the * most promising combinations of join bitmap index paths. Our strategy * is to generate one such path for each distinct parameterization seen * among the available bitmap index paths. This may look pretty * expensive, but usually there won't be very many distinct * parameterizations. (This logic is quite similar to that in * consider_index_join_clauses, but we're working with whole paths not * individual clauses.) */ if (bitjoinpaths != NIL)//bitjoinpaths位圖連接訪問路徑 { List *path_outer;//依賴的外部Relids鏈表 List *all_path_outers;//依賴的外部路徑Relids鏈表 ListCell *lc;//臨時變量 /* * path_outer holds the parameterization of each path in bitjoinpaths * (to save recalculating that several times), while all_path_outers * holds all distinct parameterization sets. */ path_outer = all_path_outers = NIL;//初始化變量 foreach(lc, bitjoinpaths)//遍歷bitjoinpaths { Path *path = (Path *) lfirst(lc);//訪問路徑 Relids required_outer;//依賴的外部Relids required_outer = get_bitmap_tree_required_outer(path);// path_outer = lappend(path_outer, required_outer);//添加到鏈表中 if (!bms_equal_any(required_outer, all_path_outers))//不等,則添加到all_path_outers中 all_path_outers = lappend(all_path_outers, required_outer); } /* Now, for each distinct parameterization set ... */ //對每一個唯一的參數化集合進行處理 foreach(lc, all_path_outers)//遍歷all_path_outers { Relids max_outers = (Relids) lfirst(lc); List *this_path_set; Path *bitmapqual; Relids required_outer; double loop_count; BitmapHeapPath *bpath; ListCell *lcp; ListCell *lco; /* Identify all the bitmap join paths needing no more than that */ this_path_set = NIL; forboth(lcp, bitjoinpaths, lco, path_outer)//遍歷 { Path *path = (Path *) lfirst(lcp); Relids p_outers = (Relids) lfirst(lco); if (bms_is_subset(p_outers, max_outers))//無需依賴其他Relids,添加到this_path_set中 this_path_set = lappend(this_path_set, path); } /* * Add in restriction bitmap paths, since they can be used * together with any join paths. */ this_path_set = list_concat(this_path_set, bitindexpaths);//合并bitindexpaths訪問路徑 /* Select best AND combination for this parameterization */ bitmapqual = choose_bitmap_and(root, rel, this_path_set);//為此參數化處理選擇最好的AND組合 /* And push that path into the mix */ required_outer = get_bitmap_tree_required_outer(bitmapqual); loop_count = get_loop_count(root, rel->relid, required_outer); bpath = create_bitmap_heap_path(root, rel, bitmapqual, required_outer, loop_count, 0);//創建索引訪問路徑 add_path(rel, (Path *) bpath); } } }
match_XXX
match_restriction_clauses_to_index函數驗證限制條件是否與Index匹配,匹配的條件添加到clauseset中.
match_join_clauses_to_index函數驗證連接條件是否與Index匹配,同樣的,匹配的條件添加到clauseset中.
match_eclass_clauses_to_index函數驗證EC連接條件是否與Index匹配,匹配的條件添加到clauseset中.
//--------------------------------------------------- match_restriction_clauses_to_index /* * match_restriction_clauses_to_index * Identify restriction clauses for the rel that match the index. * Matching clauses are added to *clauseset. * 驗證限制條件是否與Index匹配,匹配的條件加入到clauseset中 */ static void match_restriction_clauses_to_index(RelOptInfo *rel, IndexOptInfo *index, IndexClauseSet *clauseset) { /* We can ignore clauses that are implied by the index predicate */ //忽略部分(條件)索引,直接調用match_clauses_to_index match_clauses_to_index(index, index->indrestrictinfo, clauseset); } //------------------------------- match_clauses_to_index /* * match_clauses_to_index * Perform match_clause_to_index() for each clause in a list. * Matching clauses are added to *clauseset. */ static void match_clauses_to_index(IndexOptInfo *index, List *clauses, IndexClauseSet *clauseset) { ListCell *lc;//臨時變量 foreach(lc, clauses)//遍歷限制條件 { RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); match_clause_to_index(index, rinfo, clauseset); } } //--------------------------------------------------- match_join_clauses_to_index /* * match_join_clauses_to_index * Identify join clauses for the rel that match the index. * Matching clauses are added to *clauseset. * Also, add any potentially usable join OR clauses to *joinorclauses. * 驗證連接條件是否與Index匹配,匹配的條件添加到clauseset中 * 另外,在joinorclauses中添加可能有用的連接條件OR子句 */ static void match_join_clauses_to_index(PlannerInfo *root, RelOptInfo *rel, IndexOptInfo *index, IndexClauseSet *clauseset, List **joinorclauses) { ListCell *lc;//臨時變量 /* Scan the rel's join clauses */ foreach(lc, rel->joininfo)//遍歷連接條件 { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); /* Check if clause can be moved to this rel */ if (!join_clause_is_movable_to(rinfo, rel)) continue; /* Potentially usable, so see if it matches the index or is an OR */ if (restriction_is_or_clause(rinfo)) *joinorclauses = lappend(*joinorclauses, rinfo); else match_clause_to_index(index, rinfo, clauseset); } } //--------------------------------------------------- match_eclass_clauses_to_index /* * match_eclass_clauses_to_index * Identify EquivalenceClass join clauses for the rel that match the index. * Matching clauses are added to *clauseset. * 驗證EC連接條件是否與Index匹配,相匹配的子句加入到clauseset中 */ static void match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index, IndexClauseSet *clauseset) { int indexcol; /* No work if rel is not in any such ECs */ if (!index->rel->has_eclass_joins)//沒有ECs,返回 return; for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)//遍歷索引列 { ec_member_matches_arg arg; List *clauses; /* Generate clauses, skipping any that join to lateral_referencers */ //生成條件子句鏈表 arg.index = index; arg.indexcol = indexcol; clauses = generate_implied_equalities_for_column(root, index->rel, ec_member_matches_indexcol, (void *) &arg, index->rel->lateral_referencers); /* * We have to check whether the results actually do match the index, * since for non-btree indexes the EC's equality operators might not * be in the index opclass (cf ec_member_matches_indexcol). */ match_clauses_to_index(index, clauses, clauseset); } } //---------------------------- generate_implied_equalities_for_column /* * generate_implied_equalities_for_column * Create EC-derived joinclauses usable with a specific column. * 創建可用于特定列的EC衍生連接條件 * * This is used by indxpath.c to extract potentially indexable joinclauses * from ECs, and can be used by foreign data wrappers for similar purposes. * We assume that only expressions in Vars of a single table are of interest, * but the caller provides a callback function to identify exactly which * such expressions it would like to know about. * * We assume that any given table/index column could appear in only one EC. * (This should be true in all but the most pathological cases, and if it * isn't, we stop on the first match anyway.) Therefore, what we return * is a redundant list of clauses equating the table/index column to each of * the other-relation values it is known to be equal to. Any one of * these clauses can be used to create a parameterized path, and there * is no value in using more than one. (But it *is* worthwhile to create * a separate parameterized path for each one, since that leads to different * join orders.) * * The caller can pass a Relids set of rels we aren't interested in joining * to, so as to save the work of creating useless clauses. */ List * generate_implied_equalities_for_column(PlannerInfo *root, RelOptInfo *rel, ec_matches_callback_type callback, void *callback_arg, Relids prohibited_rels) { List *result = NIL;//結果鏈表 bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);//是否子Relation Relids parent_relids;//父Relids ListCell *lc1;//變量 /* Indexes are available only on base or "other" member relations. */ Assert(IS_SIMPLE_REL(rel)); /* If it's a child rel, we'll need to know what its parent(s) are */ if (is_child_rel) parent_relids = find_childrel_parents(root, rel); else parent_relids = NULL; /* not used, but keep compiler quiet */ foreach(lc1, root->eq_classes)//遍歷EC { EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);//當前的EC EquivalenceMember *cur_em;//EC成員 ListCell *lc2;//鏈表成員 /* * Won't generate joinclauses if const or single-member (the latter * test covers the volatile case too) */ if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1) continue; /* * No point in searching if rel not mentioned in eclass (but we can't * tell that for a child rel). */ if (!is_child_rel && !bms_is_subset(rel->relids, cur_ec->ec_relids)) continue; /* * Scan members, looking for a match to the target column. Note that * child EC members are considered, but only when they belong to the * target relation. (Unlike regular members, the same expression * could be a child member of more than one EC. Therefore, it's * potentially order-dependent which EC a child relation's target * column gets matched to. This is annoying but it only happens in * corner cases, so for now we live with just reporting the first * match. See also get_eclass_for_sort_expr.) */ cur_em = NULL; foreach(lc2, cur_ec->ec_members)//遍歷EC的成員 { cur_em = (EquivalenceMember *) lfirst(lc2);//當前成員 if (bms_equal(cur_em->em_relids, rel->relids) && callback(root, rel, cur_ec, cur_em, callback_arg))//調用ec_member_matches_indexcol函數 break;//找到匹配的成員,跳出 cur_em = NULL; } if (!cur_em) continue; /* * Found our match. Scan the other EC members and attempt to generate * joinclauses. */ foreach(lc2, cur_ec->ec_members) { EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2); Oid eq_op; RestrictInfo *rinfo; if (other_em->em_is_child)// continue; /* 忽略子成員,ignore children here */ /* Make sure it'll be a join to a different rel */ if (other_em == cur_em || bms_overlap(other_em->em_relids, rel->relids))//過濾cur_em continue; /* Forget it if caller doesn't want joins to this rel */ if (bms_overlap(other_em->em_relids, prohibited_rels)) continue; /* * Also, if this is a child rel, avoid generating a useless join * to its parent rel(s). */ if (is_child_rel && bms_overlap(parent_relids, other_em->em_relids)) continue; eq_op = select_equality_operator(cur_ec, cur_em->em_datatype, other_em->em_datatype); if (!OidIsValid(eq_op)) continue; /* set parent_ec to mark as redundant with other joinclauses */ rinfo = create_join_clause(root, cur_ec, eq_op, cur_em, other_em, cur_ec);//創建連接條件語句 result = lappend(result, rinfo); } /* * If somehow we failed to create any join clauses, we might as well * keep scanning the ECs for another match. But if we did make any, * we're done, because we don't want to return non-redundant clauses. */ if (result) break; } return result; } //---------------------------- match_clause_to_index /* * match_clause_to_index * Test whether a qual clause can be used with an index. * * If the clause is usable, add it to the appropriate list in *clauseset. * *clauseset must be initialized to zeroes before first call. * * Note: in some circumstances we may find the same RestrictInfos coming from * multiple places. Defend against redundant outputs by refusing to add a * clause twice (pointer equality should be a good enough check for this). * * Note: it's possible that a badly-defined index could have multiple matching * columns. We always select the first match if so; this avoids scenarios * wherein we get an inflated idea of the index's selectivity by using the * same clause multiple times with different index columns. */ static void match_clause_to_index(IndexOptInfo *index, RestrictInfo *rinfo, IndexClauseSet *clauseset) { int indexcol; /* * Never match pseudoconstants to indexes. (Normally a match could not * happen anyway, since a pseudoconstant clause couldn't contain a Var, * but what if someone builds an expression index on a constant? It's not * totally unreasonable to do so with a partial index, either.) */ if (rinfo->pseudoconstant) return; /* * If clause can't be used as an indexqual because it must wait till after * some lower-security-level restriction clause, reject it. */ if (!restriction_is_securely_promotable(rinfo, index->rel)) return; /* OK, check each index key column for a match */ for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) { if (match_clause_to_indexcol(index, indexcol, rinfo)) { clauseset->indexclauses[indexcol] = list_append_unique_ptr(clauseset->indexclauses[indexcol], rinfo);//賦值 clauseset->nonempty = true;//設置標記 return; } } } //------------------- match_clause_to_indexcol /* * match_clause_to_indexcol() * Determines whether a restriction clause matches a column of an index. * 判斷約束條件是否與索引中的某一列匹配 * * To match an index normally, the clause: * 通常來說,匹配索引,子句必須: * (1) must be in the form (indexkey op const) or (const op indexkey); * and * 滿足格式:(索引鍵 操作符 常量) 或者 (常量 操作符 索引鍵),而且 * (2) must contain an operator which is in the same family as the index * operator for this column, or is a "special" operator as recognized * by match_special_index_operator(); * and * 包含一種與索引列同一family的操作符,或者是一種通過 match_special_index_operator方法認定的特殊操作符 * (3) must match the collation of the index, if collation is relevant. * 與索引的排序規則collation匹配 * * Our definition of "const" is exceedingly liberal: we allow anything that * doesn't involve a volatile function or a Var of the index's relation. * In particular, Vars belonging to other relations of the query are * accepted here, since a clause of that form can be used in a * parameterized indexscan. It's the responsibility of higher code levels * to manage restriction and join clauses appropriately. * 這里"const"常量的定義非常自由:除了易變函數或索引關系的Var之外的,均視為"const" * 由于存在參數化索引掃描的可能,因此查詢中屬于其他Relations的Vars也可以在此出現. * 調用此函數的代碼有責任"合適"的管理限制條件和連接條件. * * Note: we do need to check for Vars of the index's relation on the * "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3)) * are not processable by a parameterized indexscan on a.f1, whereas * something like (a.f1 OP (b.f2 OP c.f3)) is. * 注意:需要在子句的const部分檢查索引關系的Vars,因為子句 * 如(a.f1 OP (b.f2 OP a.f3)不能通過a上的參數化索引掃描進行處理 * * Presently, the executor can only deal with indexquals that have the * indexkey on the left, so we can only use clauses that have the indexkey * on the right if we can commute the clause to put the key on the left. * We do not actually do the commuting here, but we check whether a * suitable commutator operator is available. * 目前為止,執行器只能處理索引鍵在左邊的索引表達式,因此只能使用那些可以 * 把索引鍵變換到左邊的條件表達式.在這個函數中不執行變換,但會執行相應的檢查. * * If the index has a collation, the clause must have the same collation. * For collation-less indexes, we assume it doesn't matter; this is * necessary for cases like "hstore ? text", wherein hstore's operators * don't care about collation but the clause will get marked with a * collation anyway because of the text argument. (This logic is * embodied in the macro IndexCollMatchesExprColl.) * 如果索引含有排序規則(collation),條件子句必須包含相同的排序規則. * 對于無collation的索引,假定collation沒有任何影響. * * It is also possible to match RowCompareExpr clauses to indexes (but * currently, only btree indexes handle this). In this routine we will * report a match if the first column of the row comparison matches the * target index column. This is sufficient to guarantee that some index * condition can be constructed from the RowCompareExpr --- whether the * remaining columns match the index too is considered in * adjust_rowcompare_for_index(). * RowCompareExpr有可能與索引進行匹配,在這個處理過程中,如果行對比的第一個列 * 與目標索引匹配,那么可以認為是匹配的. * * It is also possible to match ScalarArrayOpExpr clauses to indexes, when * the clause is of the form "indexkey op ANY (arrayconst)". * 如果子句的格式是"indexkey op ANY (arrayconst)",那么匹配ScalarArrayOpExpr * 也是可能的. * * For boolean indexes, it is also possible to match the clause directly * to the indexkey; or perhaps the clause is (NOT indexkey). * 對于布爾索引,可以直接與索引鍵進行匹配 * * 輸入參數: * 'index' is the index of interest. * index-正在處理的索引 * 'indexcol' is a column number of 'index' (counting from 0). * indexcol-索引列(從0起算) * 'rinfo' is the clause to be tested (as a RestrictInfo node). * rinfo-RestrictInfo Node * * Returns true if the clause can be used with this index key. * 如可以使用索引,則返回T * * NOTE: returns false if clause is an OR or AND clause; it is the * responsibility of higher-level routines to cope with those. * 注意:如果條件語句是OR/AND語句,則返回F,由上層處理邏輯處理 */ static bool match_clause_to_indexcol(IndexOptInfo *index, int indexcol, RestrictInfo *rinfo) { Expr *clause = rinfo->clause;//條件語句 Index index_relid = index->rel->relid;//Index的Relid Oid opfamily;//操作符種類 Oid idxcollation;//索引排序規則 Node *leftop,//左節點 *rightop;//右節點 Relids left_relids;//左節點相關Relids Relids right_relids;//右節點相關Relids Oid expr_op;//表達式操作符的Oid Oid expr_coll;//表達式Collation的Oid bool plain_op;//是否Plain操作符 Assert(indexcol < index->nkeycolumns); opfamily = index->opfamily[indexcol];//獲取操作符種類 idxcollation = index->indexcollations[indexcol];//獲取索引排序規則 /* First check for boolean-index cases. */ if (IsBooleanOpfamily(opfamily))//是否布爾類 { if (match_boolean_index_clause((Node *) clause, indexcol, index))//是否匹配 return true;//如匹配,返回T } /* * Clause must be a binary opclause, or possibly a ScalarArrayOpExpr * (which is always binary, by definition). Or it could be a * RowCompareExpr, which we pass off to match_rowcompare_to_indexcol(). * Or, if the index supports it, we can handle IS NULL/NOT NULL clauses. */ if (is_opclause(clause))//OpExpr { leftop = get_leftop(clause); rightop = get_rightop(clause); if (!leftop || !rightop) return false; left_relids = rinfo->left_relids; right_relids = rinfo->right_relids; expr_op = ((OpExpr *) clause)->opno; expr_coll = ((OpExpr *) clause)->inputcollid; plain_op = true; } else if (clause && IsA(clause, ScalarArrayOpExpr))//ScalarArrayOpExpr { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; /* We only accept ANY clauses, not ALL */ if (!saop->useOr) return false; leftop = (Node *) linitial(saop->args); rightop = (Node *) lsecond(saop->args); left_relids = NULL; /* not actually needed */ right_relids = pull_varnos(rightop); expr_op = saop->opno; expr_coll = saop->inputcollid; plain_op = false; } else if (clause && IsA(clause, RowCompareExpr))//RowCompareExpr { return match_rowcompare_to_indexcol(index, indexcol, opfamily, idxcollation, (RowCompareExpr *) clause); } else if (index->amsearchnulls && IsA(clause, NullTest))//NullTest { NullTest *nt = (NullTest *) clause; if (!nt->argisrow && match_index_to_operand((Node *) nt->arg, indexcol, index)) return true; return false; } else return false; /* * Check for clauses of the form: (indexkey operator constant) or * (constant operator indexkey). See above notes about const-ness. * (indexkey operator constant)和(constant operator indexkey)格式的語句 */ //處理:(indexkey operator constant) if (match_index_to_operand(leftop, indexcol, index) && !bms_is_member(index_relid, right_relids) && !contain_volatile_functions(rightop)) { if (IndexCollMatchesExprColl(idxcollation, expr_coll) && is_indexable_operator(expr_op, opfamily, true))//排序規則&操作符種類匹配 return true;//返回T /* * If we didn't find a member of the index's opfamily, see whether it * is a "special" indexable operator. */ if (plain_op && match_special_index_operator(clause, opfamily, idxcollation, true))//Plain操作&特殊操作符,返回T return true; return false;//否則,返回F } //處理(constant operator indexkey) if (plain_op && match_index_to_operand(rightop, indexcol, index) && !bms_is_member(index_relid, left_relids) && !contain_volatile_functions(leftop)) { if (IndexCollMatchesExprColl(idxcollation, expr_coll) && is_indexable_operator(expr_op, opfamily, false)) return true; /* * If we didn't find a member of the index's opfamily, see whether it * is a "special" indexable operator. */ if (match_special_index_operator(clause, opfamily, idxcollation, false)) return true; return false; } return false; }
測試腳本如下
select a.*,b.grbh,b.je from t_dwxx a, lateral (select t1.dwbh,t1.grbh,t2.je from t_grxx t1 inner join t_jfxx t2 on t1.dwbh = a.dwbh and t1.grbh = t2.grbh) b where a.dwbh = '1001' order by b.dwbh;
注意:按先前的分析,SQL語句存在等價類{t_dwxx.dwbh t_grxx.dwbh '1001'}和{t_grxx.grbh t_jfxx.grbh},在構造t_grxx的索引訪問路徑時,使用等價類構造.
啟動gdb,第一個RelOptInfo(對應t_dwxx)有3個Index,第二個RelOptInfo(對應t_grxx)有2個Index(分別是在dwbh和grbh上的索引),第三個RelOptInfo(對應t_jfxx)有1個Index(grbh上的索引),本節以t_jfxx和t_grxx為例進行跟蹤分析
... (gdb) c Continuing. Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:242 242 if (rel->indexlist == NIL) (gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value $38 = {type = T_IndexOptInfo, indexoid = 16750, reltablespace = 0, rel = 0x2729530, pages = 276, tuples = 100000, tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729998, indexcollations = 0x27299b0, opfamily = 0x27299c8, opcintype = 0x27299e0, sortopfamily = 0x27299c8, reverse_sort = 0x2729a10, nulls_first = 0x2729a28, canreturn = 0x27299f8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x2729ae0, indrestrictinfo = 0x0, predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true, amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true, amcostestimate = 0x94f0ad <btcostestimate>}
輸入信息是已熟知的root(PlannerInfo)和rel(RelOptInfo).首先進行索引遍歷循環
(gdb) c Continuing. Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:242 242 if (rel->indexlist == NIL) (gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value $38 = {type = T_IndexOptInfo, indexoid = 16750, reltablespace = 0, rel = 0x2729530, pages = 276, tuples = 100000, tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729998, indexcollations = 0x27299b0, opfamily = 0x27299c8, opcintype = 0x27299e0, sortopfamily = 0x27299c8, reverse_sort = 0x2729a10, nulls_first = 0x2729a28, canreturn = 0x27299f8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x2729ae0, indrestrictinfo = 0x0, predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true, amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true, amcostestimate = 0x94f0ad <btcostestimate>}
查詢數據字典pg_class,oid=16750相應的索引是idx_t_jfxx_grbh
testdb=# select relname from pg_class where oid=16750; relname ----------------- idx_t_jfxx_grbh (1 row)
調用match_restriction_clauses_to_index和match_join_clauses_to_index,子句集合均為NULL
(gdb) match_restriction_clauses_to_index (rel=0x2729530, index=0x2729888, clauseset=0x7fff69cf0890) at indxpath.c:2117 2117 } (gdb) create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:275 275 get_index_paths(root, rel, index, &rclauseset, (gdb) 284 MemSet(&jclauseset, 0, sizeof(jclauseset)); (gdb) 285 match_join_clauses_to_index(root, rel, index, (gdb) 292 MemSet(&eclauseset, 0, sizeof(eclauseset)); (gdb) 293 match_eclass_clauses_to_index(root, index, (gdb) p rclauseset $2 = {nonempty = false, indexclauses = {0x0 <repeats 32 times>}} (gdb) p joinorclauses $3 = (List *) 0x0 (gdb) p jclauseset $4 = {nonempty = false, indexclauses = {0x0 <repeats 32 times>}}
進入match_eclass_clauses_to_index
... 268 match_restriction_clauses_to_index(rel, index, &rclauseset); (gdb) step match_restriction_clauses_to_index (rel=0x2724c88, index=0x27254d8, clauseset=0x7fff69cf0890) at indxpath.c:2116 2116 match_clauses_to_index(index, index->indrestrictinfo, clauseset);
進入generate_implied_equalities_for_column
... (gdb) step generate_implied_equalities_for_column (root=0x2714c50, rel=0x2729530, callback=0x7509b0 <ec_member_matches_indexcol>, callback_arg=0x7fff69cf0620, prohibited_rels=0x0) at equivclass.c:2219 2219 List *result = NIL;
等價類信息
... 2235 EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); (gdb) 2243 if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1) (gdb) p *cur_ec $6 = {type = T_EquivalenceClass, ec_opfamilies = 0x272a268, ec_collation = 100, ec_members = 0x272a4a8, ec_sources = 0x272a3f0, ec_derives = 0x272d2f0, ec_relids = 0x272a470, ec_has_const = false, ec_has_volatile = false, ec_below_outer_join = false, ec_broken = false, ec_sortref = 0, ec_min_security = 0, ec_max_security = 0, ec_merged = 0x0}
遍歷EC的成員后,cur_em不為NULL,查看cur_em內存結構(匹配的成員,即t_jfxx.grbh)
2281 foreach(lc2, cur_ec->ec_members) (gdb) p *cur_em $7 = {type = T_EquivalenceMember, em_expr = 0x2722890, em_relids = 0x272a238, em_nullable_relids = 0x0, em_is_const = false, em_is_child = false, em_datatype = 25} (gdb) p *cur_em->em_expr $8 = {type = T_RelabelType} (gdb) p *(RelabelType *)cur_em->em_expr $9 = {xpr = {type = T_RelabelType}, arg = 0x2722840, resulttype = 25, resulttypmod = -1, resultcollid = 100, relabelformat = COERCE_IMPLICIT_CAST, location = -1} (gdb) p *((RelabelType *)cur_em->em_expr)->arg $10 = {type = T_Var} (gdb) p *(Var *)((RelabelType *)cur_em->em_expr)->arg $11 = {xpr = {type = T_Var}, varno = 4, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, varnoold = 4, varoattno = 1, location = 168}
再次遍歷等價類的成員,得到第一個約束條件(t_jfxx.grbh=t_grxx.grbh)
(gdb) n 2314 rinfo = create_join_clause(root, cur_ec, eq_op, (gdb) 2318 result = lappend(result, rinfo); (gdb) p *rinfo $18 = {type = T_RestrictInfo, clause = 0x272d910, is_pushed_down = true, outerjoin_delayed = false, can_join = true, pseudoconstant = false, leakproof = false, security_level = 0, clause_relids = 0x272db10, required_relids = 0x272d5f0, outer_relids = 0x0, nullable_relids = 0x0, left_relids = 0x272dae0, right_relids = 0x272daf8, orclause = 0x0, parent_ec = 0x272a340, eval_cost = {startup = 0, per_tuple = 0.0025000000000000001}, norm_selec = -1, outer_selec = -1, mergeopfamilies = 0x272db48, left_ec = 0x272a340, right_ec = 0x272a340, left_em = 0x272a4d8, right_em = 0x272a420, scansel_cache = 0x0, outer_is_left = false, hashjoinoperator = 98, left_bucketsize = -1, right_bucketsize = -1, left_mcvfreq = -1, right_mcvfreq = -1} (gdb) set $tmp1=(RelabelType *)((OpExpr *)rinfo->clause)->args->head->data.ptr_value (gdb) set $tmp2=(RelabelType *)((OpExpr *)rinfo->clause)->args->head->next->data.ptr_value (gdb) p *(Var *)$tmp1->arg $31 = {xpr = {type = T_Var}, varno = 4, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, varnoold = 4, varoattno = 1, location = 168} (gdb) p *(Var *)$tmp2->arg $32 = {xpr = {type = T_Var}, varno = 3, varattno = 2, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, varnoold = 3, varoattno = 2, location = 158}
獲得了結果,返回到match_eclass_clauses_to_index
2281 foreach(lc2, cur_ec->ec_members) (gdb) 2326 if (result) (gdb) 2327 break; (gdb) 2330 return result; (gdb) 2331 } (gdb) match_eclass_clauses_to_index (root=0x2714c50, index=0x2729888, clauseset=0x7fff69cf0670) at indxpath.c:2184 2184 match_clauses_to_index(index, clauses, clauseset); ...
下面再考察t_grxx.dwbh上的索引為例,分析match_clause_to_index
(gdb) c Continuing. Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2728c38) at indxpath.c:242 242 if (rel->indexlist == NIL) (gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value $39 = {type = T_IndexOptInfo, indexoid = 16752, reltablespace = 0, rel = 0x2728c38, pages = 276, tuples = 100000, tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729378, indexcollations = 0x2729390, opfamily = 0x27293a8, opcintype = 0x27293c0, sortopfamily = 0x27293a8, reverse_sort = 0x27293f0, nulls_first = 0x2729408, canreturn = 0x27293d8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x27294e0, indrestrictinfo = 0x272b040, predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true, amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true, amcostestimate = 0x94f0ad <btcostestimate>}
oid=16752,對應的object為idx_t_grxx_dwbh
testdb=# select relname from pg_class where oid=16752; relname ----------------- idx_t_grxx_dwbh (1 row)
進入IndexOptInfo循環,第一個元素對應的IndexOptInfo為idx_t_grxx_dwbh
249 foreach(lc, rel->indexlist) (gdb) p *rel->indexlist $40 = {type = T_List, length = 2, head = 0x2729510, tail = 0x2729218} (gdb) p *(IndexOptInfo *)rel->indexlist->head->data->ptr_value $42 = {type = T_IndexOptInfo, indexoid = 16752, reltablespace = 0, rel = 0x2728c38, pages = 276, tuples = 100000, tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729378, indexcollations = 0x2729390, opfamily = 0x27293a8, opcintype = 0x27293c0, sortopfamily = 0x27293a8, reverse_sort = 0x27293f0, nulls_first = 0x2729408, canreturn = 0x27293d8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x27294e0, indrestrictinfo = 0x272b040, predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true, amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true, amcostestimate = 0x94f0ad <btcostestimate>}
一路小跑,進入match_clause_to_indexcol
... (gdb) step match_clause_to_indexcol (index=0x2729268, indexcol=0, rinfo=0x272ae58) at indxpath.c:2330 2330 Expr *clause = rinfo->clause; (gdb) n 2331 Index index_relid = index->rel->relid; (gdb) n 2344 opfamily = index->opfamily[indexcol]; (gdb) 2345 idxcollation = index->indexcollations[indexcol]; (gdb) p index_relid $47 = 3 (gdb) p opfamily $48 = 1994 (gdb)
根據opfamily查詢數據字典
testdb=# select * from pg_opfamily where oid=1994; opfmethod | opfname | opfnamespace | opfowner -----------+----------+--------------+---------- 403 | text_ops | 11 | 10 (1 row) -- 索引訪問方法(btree) testdb=# select * from pg_am where oid=403; amname | amhandler | amtype --------+-----------+-------- btree | bthandler | i (1 row)
下面進入is_opclause判斷分支
(gdb) p idxcollation $49 = 100 (gdb) n 2360 if (is_opclause(clause)) (gdb) 2362 leftop = get_leftop(clause); (gdb) 2363 rightop = get_rightop(clause); (gdb) 2364 if (!leftop || !rightop) (gdb) p *leftop $50 = {type = T_RelabelType} (gdb) p *rightop $51 = {type = T_Const}
限制條件下推后,形成限制條件t_grxx.dwbh = '1001'
#Var:t_grxx.dwbh (gdb) p *(RelabelType *)leftop $56 = {xpr = {type = T_RelabelType}, arg = 0x272ad80, resulttype = 25, resulttypmod = -1, resultcollid = 100, relabelformat = COERCE_IMPLICIT_CAST, location = -1} #常量:'1001' (gdb) p *(Const *)rightop $57 = {xpr = {type = T_Const}, consttype = 25, consttypmod = -1, constcollid = 100, constlen = -1, constvalue = 41069848, constisnull = false, constbyval = false, location = 194}
執行相關判斷,返回T
(gdb) n 2366 left_relids = rinfo->left_relids; (gdb) 2367 right_relids = rinfo->right_relids; (gdb) 2368 expr_op = ((OpExpr *) clause)->opno; (gdb) 2369 expr_coll = ((OpExpr *) clause)->inputcollid; (gdb) 2370 plain_op = true; (gdb) 2409 if (match_index_to_operand(leftop, indexcol, index) && (gdb) 2410 !bms_is_member(index_relid, right_relids) && (gdb) 2409 if (match_index_to_operand(leftop, indexcol, index) && (gdb) 2411 !contain_volatile_functions(rightop)) (gdb) 2410 !bms_is_member(index_relid, right_relids) && (gdb) 2413 if (IndexCollMatchesExprColl(idxcollation, expr_coll) && (gdb) 2414 is_indexable_operator(expr_op, opfamily, true)) (gdb) 2413 if (IndexCollMatchesExprColl(idxcollation, expr_coll) && (gdb) 2415 return true;
給clauseset變量賦值
(gdb) match_clause_to_index (index=0x2729268, rinfo=0x272ae58, clauseset=0x7fff69cf0890) at indxpath.c:2255 2255 list_append_unique_ptr(clauseset->indexclauses[indexcol], (gdb) 2254 clauseset->indexclauses[indexcol] = (gdb) 2257 clauseset->nonempty = true; (gdb) 2258 return; (gdb) 2261 }
返回到match_clauses_to_index
(gdb) match_clauses_to_index (index=0x2729268, clauses=0x272b040, clauseset=0x7fff69cf0890) at indxpath.c:2200 2200 foreach(lc, clauses)
到此,關于“PostgreSQL中set_base_rel_pathlists函數有什么作用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。