PostgreSQL 原始碼解讀(55)- 查詢語句#40(make_one_rel函式#5-索...
本節繼續介紹make_one_rel函式中的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)
至此,match_XXX的跟蹤完畢,下一小節介紹get_index_paths.
四、參考資料
allpaths.c
cost.h
costsize.c
PG Document:Query Planning
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374853/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PostgreSQL 原始碼解讀(70)- 查詢語句#55(make_one_rel函式#20-...SQL原始碼函式
- PostgreSQL 原始碼解讀(56)- 查詢語句#41(make_one_rel函式#6-索...SQL原始碼函式
- PostgreSQL 原始碼解讀(57)- 查詢語句#42(make_one_rel函式#7-索...SQL原始碼函式
- PostgreSQL 原始碼解讀(71)- 查詢語句#56(make_one_rel函式#21-...SQL原始碼函式
- PostgreSQL 原始碼解讀(67)- 查詢語句#52(make_one_rel函式#17-...SQL原始碼函式
- PostgreSQL 原始碼解讀(68)- 查詢語句#53(make_one_rel函式#18-...SQL原始碼函式
- PostgreSQL 原始碼解讀(69)- 查詢語句#54(make_one_rel函式#19-...SQL原始碼函式
- PostgreSQL 原始碼解讀(66)- 查詢語句#51(make_one_rel函式#16-...SQL原始碼函式
- PostgreSQL 原始碼解讀(72)- 查詢語句#57(make_one_rel函式#22-...SQL原始碼函式
- PostgreSQL 原始碼解讀(65)- 查詢語句#50(make_one_rel函式#15-...SQL原始碼函式
- PostgreSQL 原始碼解讀(62)- 查詢語句#47(make_one_rel函式#12-...SQL原始碼函式
- PostgreSQL 原始碼解讀(63)- 查詢語句#48(make_one_rel函式#13-...SQL原始碼函式
- PostgreSQL 原始碼解讀(64)- 查詢語句#49(make_one_rel函式#14-...SQL原始碼函式
- PostgreSQL 原始碼解讀(60)- 查詢語句#45(make_one_rel函式#10-...SQL原始碼函式
- PostgreSQL 原始碼解讀(61)- 查詢語句#46(make_one_rel函式#11-...SQL原始碼函式
- PostgreSQL 原始碼解讀(40)- 查詢語句#25(query_planner函式#3)SQL原始碼函式
- PostgreSQL 原始碼解讀(52)- 查詢語句#37(make_one_rel函式#2-估...SQL原始碼函式
- PostgreSQL 原始碼解讀(53)- 查詢語句#38(make_one_rel函式#3-順...SQL原始碼函式
- PostgreSQL 原始碼解讀(54)- 查詢語句#39(make_one_rel函式#4-生...SQL原始碼函式
- PostgreSQL 原始碼解讀(58)- 查詢語句#43(make_one_rel函式#8-B...SQL原始碼函式
- PostgreSQL 原始碼解讀(59)- 查詢語句#44(make_one_rel函式#9-B...SQL原始碼函式
- PostgreSQL 原始碼解讀(49)- 查詢語句#34(make_one_rel函式#1-概覽)SQL原始碼函式
- PostgreSQL 原始碼解讀(83)- 查詢語句#68(PortalStart函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(82)- 查詢語句#67(PortalXXX系列函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(89)- 查詢語句#74(SeqNext函式#2)SQL原始碼函式
- PostgreSQL 原始碼解讀(90)- 查詢語句#75(ExecHashJoin函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(91)- 查詢語句#76(ExecHashJoin函式#2)SQL原始碼函式
- PostgreSQL 原始碼解讀(88)- 查詢語句#73(SeqNext函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(93)- 查詢語句#77(ExecHashJoin函式#3)SQL原始碼函式
- PostgreSQL 原始碼解讀(52)- 查詢語句#37(make_one_rel函式#2-估算關係大小)SQL原始碼函式
- PostgreSQL 原始碼解讀(73)- 查詢語句#58(grouping_planner函式...SQL原始碼函式
- PostgreSQL 原始碼解讀(95)- 查詢語句#78(ExecHashJoin函式#4-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(97)- 查詢語句#79(ExecHashJoin函式#5-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(43)- 查詢語句#28(query_planner函式#5)SQL原始碼函式
- PostgreSQL 原始碼解讀(45)- 查詢語句#30(query_planner函式#6)SQL原始碼函式
- PostgreSQL 原始碼解讀(46)- 查詢語句#31(query_planner函式#7)SQL原始碼函式
- PostgreSQL 原始碼解讀(47)- 查詢語句#32(query_planner函式#8)SQL原始碼函式
- PostgreSQL 原始碼解讀(48)- 查詢語句#33(query_planner函式#9)SQL原始碼函式