本節(jié)介紹了創(chuàng)建計(jì)劃create_plan函數(shù)中掃描計(jì)劃的實(shí)現(xiàn)過程惑灵,主要的邏輯在函數(shù)create_scan_plan中實(shí)現(xiàn)池凄。
一、數(shù)據(jù)結(jié)構(gòu)
Plan
所有計(jì)劃節(jié)點(diǎn)通過將Plan結(jié)構(gòu)作為第一個(gè)字段從Plan結(jié)構(gòu)“派生”蕉饼。這確保了在將節(jié)點(diǎn)轉(zhuǎn)換為計(jì)劃節(jié)點(diǎn)時(shí),一切都能正常工作丰辣。(在執(zhí)行器中以通用方式傳遞時(shí)摆马,節(jié)點(diǎn)指針經(jīng)常被轉(zhuǎn)換為Plan *)
/* ----------------
* Plan node
*
* All plan nodes "derive" from the Plan structure by having the
* Plan structure as the first field. This ensures that everything works
* when nodes are cast to Plan's. (node pointers are frequently cast to Plan*
* when passed around generically in the executor)
* 所有計(jì)劃節(jié)點(diǎn)通過將Plan結(jié)構(gòu)作為第一個(gè)字段從Plan結(jié)構(gòu)“派生”激蹲。
* 這確保了在將節(jié)點(diǎn)轉(zhuǎn)換為計(jì)劃節(jié)點(diǎn)時(shí)火本,一切都能正常工作见秽。
* (在執(zhí)行器中以通用方式傳遞時(shí)舟陆,節(jié)點(diǎn)指針經(jīng)常被轉(zhuǎn)換為Plan *)
*
* We never actually instantiate any Plan nodes; this is just the common
* abstract superclass for all Plan-type nodes.
* 從未實(shí)例化任何Plan節(jié)點(diǎn);這只是所有Plan-type節(jié)點(diǎn)的通用抽象超類慧耍。
* ----------------
*/
typedef struct Plan
{
NodeTag type;//節(jié)點(diǎn)類型
/*
* 成本估算信息;estimated execution costs for plan (see costsize.c for more info)
*/
Cost startup_cost; /* 啟動(dòng)成本;cost expended before fetching any tuples */
Cost total_cost; /* 總成本;total cost (assuming all tuples fetched) */
/*
* 優(yōu)化器估算信息;planner's estimate of result size of this plan step
*/
double plan_rows; /* 行數(shù);number of rows plan is expected to emit */
int plan_width; /* 平均行大小(Byte為單位);average row width in bytes */
/*
* 并行執(zhí)行相關(guān)的信息;information needed for parallel query
*/
bool parallel_aware; /* 是否參與并行執(zhí)行邏輯?engage parallel-aware logic? */
bool parallel_safe; /* 是否并行安全;OK to use as part of parallel plan? */
/*
* Plan類型節(jié)點(diǎn)通用的信息.Common structural data for all Plan types.
*/
int plan_node_id; /* unique across entire final plan tree */
List *targetlist; /* target list to be computed at this node */
List *qual; /* implicitly-ANDed qual conditions */
struct Plan *lefttree; /* input plan tree(s) */
struct Plan *righttree;
List *initPlan; /* Init Plan nodes (un-correlated expr
* subselects) */
/*
* Information for management of parameter-change-driven rescanning
* parameter-change-driven重掃描的管理信息.
*
* extParam includes the paramIDs of all external PARAM_EXEC params
* affecting this plan node or its children. setParam params from the
* node's initPlans are not included, but their extParams are.
*
* allParam includes all the extParam paramIDs, plus the IDs of local
* params that affect the node (i.e., the setParams of its initplans).
* These are _all_ the PARAM_EXEC params that affect this node.
*/
Bitmapset *extParam;
Bitmapset *allParam;
} Plan;
二贞远、源碼解讀
create_scan_plan函數(shù)創(chuàng)建Scan Plan.掃描可以分為順序掃描(全表掃描)/索引掃描/索引快速掃描/TID掃描等多種掃描方式,這里主要介紹常見的順序掃描和索引掃描,相應(yīng)的實(shí)現(xiàn)函數(shù)是create_seqscan_plan和create_indexscan_plan.
//--------------------------------------------------- create_scan_plan
/*
* create_scan_plan
* Create a scan plan for the parent relation of 'best_path'.
* 為relation best_path創(chuàng)建相應(yīng)的掃描計(jì)劃
*/
static Plan *
create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
{
RelOptInfo *rel = best_path->parent;
List *scan_clauses;
List *gating_clauses;
List *tlist;
Plan *plan;
/*
* Extract the relevant restriction clauses from the parent relation. The
* executor must apply all these restrictions during the scan, except for
* pseudoconstants which we'll take care of below.
* 從父關(guān)系中提取相關(guān)的限制條件棚饵。
* 執(zhí)行器必須在掃描期間應(yīng)用所有這些限制條件垦写,除了偽常量,將在下面處理這些限制彰触。
*
* If this is a plain indexscan or index-only scan, we need not consider
* restriction clauses that are implied by the index's predicate, so use
* indrestrictinfo not baserestrictinfo. Note that we can't do that for
* bitmap indexscans, since there's not necessarily a single index
* involved; but it doesn't matter since create_bitmap_scan_plan() will be
* able to get rid of such clauses anyway via predicate proof.
* 如果這是一個(gè)普通的indexscan或index-only掃描梯投,
* 則不需要考慮由索引謂詞隱含的限制條款,因此使用indrestrictinfo而不是baserestrictinfo况毅。
* 注意分蓖,對(duì)于位圖索引掃描,我們不能這樣做俭茧,因?yàn)椴恍枰粋€(gè)索引;
* 但是這并不重要咆疗,因?yàn)閏reate_bitmap_scan_plan()將能夠通過謂詞證明消除這些子句。
*/
switch (best_path->pathtype)
{
case T_IndexScan:
case T_IndexOnlyScan://索引掃描,使用索引約束條件
scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
break;
default:
scan_clauses = rel->baserestrictinfo;//默認(rèn)使用關(guān)系中的約束條件
break;
}
/*
* If this is a parameterized scan, we also need to enforce all the join
* clauses available from the outer relation(s).
* 如果這是一個(gè)參數(shù)化掃描,需要從連接外關(guān)系中加入所有可用的連接約束條件
*
* For paranoia's sake, don't modify the stored baserestrictinfo list.
* 由于paranoia's sake,不更新baserestrictinfo鏈表
*/
if (best_path->param_info)
scan_clauses = list_concat(list_copy(scan_clauses),
best_path->param_info->ppi_clauses);
/*
* Detect whether we have any pseudoconstant quals to deal with. Then, if
* we'll need a gating Result node, it will be able to project, so there
* are no requirements on the child's tlist.
* 檢測(cè)是否有偽常數(shù)函數(shù)需要處理母债。
* 然后,需要一個(gè)能夠執(zhí)行投影的出口結(jié)果節(jié)點(diǎn)尝抖,因此對(duì)子tlist沒有需求毡们。
*/
gating_clauses = get_gating_quals(root, scan_clauses);
if (gating_clauses)
flags = 0;
/*
* For table scans, rather than using the relation targetlist (which is
* only those Vars actually needed by the query), we prefer to generate a
* tlist containing all Vars in order. This will allow the executor to
* optimize away projection of the table tuples, if possible.
* 對(duì)于表掃描,不使用關(guān)系targetlist(它只是查詢實(shí)際需要的Vars)昧辽,
* 我們更喜歡按照順序生成一個(gè)包含所有Vars的tlist衙熔。
* 如果可能的話,這將允許執(zhí)行程序優(yōu)化表元組的投影搅荞。
*
* But if the caller is going to ignore our tlist anyway, then don't
* bother generating one at all. We use an exact equality test here, so
* that this only applies when CP_IGNORE_TLIST is the only flag set.
* 但是红氯,如果調(diào)用者不管怎樣都要忽略tlist,那么就根本不用去生成一個(gè)咕痛。
* 在這里使用了一個(gè)完全相等的測(cè)試痢甘,因此只有當(dāng)CP_IGNORE_TLIST是唯一的標(biāo)志設(shè)置時(shí)才適用。
*/
if (flags == CP_IGNORE_TLIST)
{
tlist = NULL;//使用CP_IGNORE_TLIST標(biāo)志,則設(shè)置tlist為NULL
}
else if (use_physical_tlist(root, best_path, flags))
{
if (best_path->pathtype == T_IndexOnlyScan)//索引快速掃描
{
/* For index-only scan, the preferred tlist is the index's */
//對(duì)于所有快速掃描,tlist中的列應(yīng)在索引中
tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
/*
* Transfer sortgroupref data to the replacement tlist, if
* requested (use_physical_tlist checked that this will work).
* 如需要,轉(zhuǎn)換sortgroupref數(shù)據(jù)為tlist(use_physical_tlist檢查是否可行)
*/
if (flags & CP_LABEL_TLIST)
apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
}
else//非索引快速掃描
{
tlist = build_physical_tlist(root, rel);//構(gòu)建物理的tlist
if (tlist == NIL)
{
/* Failed because of dropped cols, so use regular method */
//build_physical_tlist無法構(gòu)建,則使用常規(guī)方法構(gòu)建
tlist = build_path_tlist(root, best_path);
}
else
{
/* As above, transfer sortgroupref data to replacement tlist */
if (flags & CP_LABEL_TLIST)
apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
}
}
}
else
{
tlist = build_path_tlist(root, best_path);//使用常規(guī)方法構(gòu)建
}
switch (best_path->pathtype)//根據(jù)路徑類型進(jìn)行相應(yīng)的處理
{
case T_SeqScan://順序掃描
plan = (Plan *) create_seqscan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_SampleScan:
plan = (Plan *) create_samplescan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_IndexScan:
plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
scan_clauses,
false);
break;
case T_IndexOnlyScan:
plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
scan_clauses,
true);
break;
case T_BitmapHeapScan:
plan = (Plan *) create_bitmap_scan_plan(root,
(BitmapHeapPath *) best_path,
tlist,
scan_clauses);
break;
case T_TidScan:
plan = (Plan *) create_tidscan_plan(root,
(TidPath *) best_path,
tlist,
scan_clauses);
break;
case T_SubqueryScan:
plan = (Plan *) create_subqueryscan_plan(root,
(SubqueryScanPath *) best_path,
tlist,
scan_clauses);
break;
case T_FunctionScan:
plan = (Plan *) create_functionscan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_TableFuncScan:
plan = (Plan *) create_tablefuncscan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_ValuesScan:
plan = (Plan *) create_valuesscan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_CteScan:
plan = (Plan *) create_ctescan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_NamedTuplestoreScan:
plan = (Plan *) create_namedtuplestorescan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_WorkTableScan:
plan = (Plan *) create_worktablescan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_ForeignScan:
plan = (Plan *) create_foreignscan_plan(root,
(ForeignPath *) best_path,
tlist,
scan_clauses);
break;
case T_CustomScan:
plan = (Plan *) create_customscan_plan(root,
(CustomPath *) best_path,
tlist,
scan_clauses);
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
plan = NULL; /* keep compiler quiet */
break;
}
/*
* If there are any pseudoconstant clauses attached to this node, insert a
* gating Result node that evaluates the pseudoconstants as one-time
* quals.
* 如果這個(gè)節(jié)點(diǎn)上附加了偽常量子句茉贡,插入一個(gè)Result節(jié)點(diǎn)塞栅,該節(jié)點(diǎn)將偽常量計(jì)算為一次性的條件quals。
*/
if (gating_clauses)
plan = create_gating_plan(root, best_path, plan, gating_clauses);
return plan;
}
//------------------------------------- create_seqscan_plan
/*
* create_seqscan_plan
* Returns a seqscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
* 返回seqscan順序掃描計(jì)劃(基于基本關(guān)系中的best_path),同時(shí)考慮了約束條件scan_clauses和投影列tlist
*/
static SeqScan *
create_seqscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses)
{
SeqScan *scan_plan;
Index scan_relid = best_path->parent->relid;
/* it should be a base rel... */
//基本關(guān)系
Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_RELATION);
/* Sort clauses into best execution order */
//約束條件排序?yàn)樽罴训膱?zhí)行順序
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
//規(guī)約限制條件信息鏈表到裸表達(dá)式,同時(shí)忽略pseudoconstants
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Replace any outer-relation variables with nestloop params */
//參數(shù)化訪問,則使用內(nèi)嵌循環(huán)參數(shù)替換外表變量
if (best_path->param_info)
{
scan_clauses = (List *)
replace_nestloop_params(root, (Node *) scan_clauses);//約束條件
}
scan_plan = make_seqscan(tlist,
scan_clauses,
scan_relid);//構(gòu)建掃描計(jì)劃
copy_generic_path_info(&scan_plan->plan, best_path);
return scan_plan;//返回
}
//------------------------------------- copy_generic_path_info
/*
* Copy cost and size info from a Path node to the Plan node created from it.
* The executor usually won't use this info, but it's needed by EXPLAIN.
* Also copy the parallel-related flags, which the executor *will* use.
*/
static void
copy_generic_path_info(Plan *dest, Path *src)
{
dest->startup_cost = src->startup_cost;
dest->total_cost = src->total_cost;
dest->plan_rows = src->rows;
dest->plan_width = src->pathtarget->width;
dest->parallel_aware = src->parallel_aware;
dest->parallel_safe = src->parallel_safe;
}
//------------------------------------- make_seqscan
static SeqScan *
make_seqscan(List *qptlist,
List *qpqual,
Index scanrelid)
{
SeqScan *node = makeNode(SeqScan);
Plan *plan = &node->plan;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scanrelid = scanrelid;
return node;//創(chuàng)建節(jié)點(diǎn)
}
//------------------------------------- create_indexscan_plan
/*
* create_indexscan_plan
* Returns an indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
* 返回索引掃描計(jì)劃
*
* We use this for both plain IndexScans and IndexOnlyScans, because the
* qual preprocessing work is the same for both. Note that the caller tells
* us which to build --- we don't look at best_path->path.pathtype, because
* create_bitmap_subplan needs to be able to override the prior decision.
* 此過程用于普通的indexscan和indexonlyscan腔丧,因?yàn)閮烧叩臈l件qual預(yù)處理工作是相同的放椰。
* 注意作烟,調(diào)用者明確指示構(gòu)建哪個(gè)——不需要查看best_path->path.pathtype,
* 因?yàn)閏reate_bitmap_subplan需要能夠覆蓋之前的決定砾医。
*/
static Scan *
create_indexscan_plan(PlannerInfo *root,
IndexPath *best_path,
List *tlist,
List *scan_clauses,
bool indexonly)
{
Scan *scan_plan;
List *indexquals = best_path->indexquals;
List *indexorderbys = best_path->indexorderbys;
Index baserelid = best_path->path.parent->relid;
Oid indexoid = best_path->indexinfo->indexoid;
List *qpqual;
List *stripped_indexquals;
List *fixed_indexquals;
List *fixed_indexorderbys;
List *indexorderbyops = NIL;
ListCell *l;
/* it should be a base rel... */
//基本關(guān)系
Assert(baserelid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
/*
* Build "stripped" indexquals structure (no RestrictInfos) to pass to
* executor as indexqualorig
* 構(gòu)建"stripped"索引約束條件結(jié)構(gòu)(非RestrictInfos),作為執(zhí)行器的調(diào)用參數(shù)indexqualorig
*/
stripped_indexquals = get_actual_clauses(indexquals);
/*
* The executor needs a copy with the indexkey on the left of each clause
* and with index Vars substituted for table ones.
* 執(zhí)行器需要在每個(gè)子句的左邊加上indexkey拿撩,并用索引變量替換表變量。
*/
fixed_indexquals = fix_indexqual_references(root, best_path);
/*
* Likewise fix up index attr references in the ORDER BY expressions.
* 同樣如蚜,修正ORDER BY 表達(dá)式的索引屬性attr引用绷雏。
*/
fixed_indexorderbys = fix_indexorderby_references(root, best_path);
/*
* The qpqual list must contain all restrictions not automatically handled
* by the index, other than pseudoconstant clauses which will be handled
* by a separate gating plan node. All the predicates in the indexquals
* will be checked (either by the index itself, or by nodeIndexscan.c),
* but if there are any "special" operators involved then they must be
* included in qpqual. The upshot is that qpqual must contain
* scan_clauses minus whatever appears in indexquals.
* qpqual鏈表必須包含索引未處理的其他限制條件,偽常量子句除外怖亭,偽常量子句將由單獨(dú)的gating計(jì)劃節(jié)點(diǎn)處理涎显。
* indexquals中的所有謂詞都將被檢查(可以通過索引本身檢查,也可以通過nodeIndexscan.c檢查)兴猩,
* 但是如果涉及到任何“特殊”運(yùn)算符期吓,那么它們必須包含在qpqual中。
* 結(jié)果是倾芝,qpqual必須包含scan_clause讨勤,除去indexquals中出現(xiàn)的任何內(nèi)容。
*
* In normal cases simple pointer equality checks will be enough to spot
* duplicate RestrictInfos, so we try that first.
* 在通常情況下晨另,簡(jiǎn)單的指針相等檢查將足以發(fā)現(xiàn)雙重的RestrictInfos潭千,因此首先執(zhí)行此檢查操作。
*
* Another common case is that a scan_clauses entry is generated from the
* same EquivalenceClass as some indexqual, and is therefore redundant
* with it, though not equal. (This happens when indxpath.c prefers a
* different derived equality than what generate_join_implied_equalities
* picked for a parameterized scan's ppi_clauses.)
* 另一種常見的情況是scan_clauses entry是由與indexqual相同的EC生成的借尿,因此盡管不相等但它是多余的刨晴。
* (這發(fā)生在indxpath.c與為參數(shù)化掃描的ppi_clauses與generate_join_implied_equalities選擇的是不同的派生等式時(shí))。
*
* In some situations (particularly with OR'd index conditions) we may
* have scan_clauses that are not equal to, but are logically implied by,
* the index quals; so we also try a predicate_implied_by() check to see
* if we can discard quals that way. (predicate_implied_by assumes its
* first input contains only immutable functions, so we have to check
* that.)
* 在某些情況下(特別是在索引條件下)路翻,可能有scan_clauses狈癞,雖然不等價(jià),但邏輯上由索引quals表示;
* 因此,我們還嘗試使用predicate_implied_by()檢驗(yàn)是否這樣丟棄quals茂契。
* (predicate_implied_by假設(shè)它的第一個(gè)輸入只包含不可變函數(shù)蝶桶,所以必須檢查它。)
*
* Note: if you change this bit of code you should also look at
* extract_nonindex_conditions() in costsize.c.
* 注意:如果需要這部分代碼,需要檢查costsize.c中的extract_nonindex_conditions()函數(shù)
*/
qpqual = NIL;
foreach(l, scan_clauses)//遍歷scan_clauses鏈表
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
if (rinfo->pseudoconstant)
continue; /* 不理會(huì)pseudoconstants;we may drop pseudoconstants here */
if (list_member_ptr(indexquals, rinfo))
continue; /* 重復(fù)不處理;simple duplicate */
if (is_redundant_derived_clause(rinfo, indexquals))
continue; /* 從EC中派生的不處理;derived from same EquivalenceClass */
if (!contain_mutable_functions((Node *) rinfo->clause) &&
predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
continue; /* 通過indexquals可隱式證明;provably implied by indexquals */
qpqual = lappend(qpqual, rinfo);
}
/* Sort clauses into best execution order */
//條件排序
qpqual = order_qual_clauses(root, qpqual);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
//規(guī)約
qpqual = extract_actual_clauses(qpqual, false);
/*
* We have to replace any outer-relation variables with nestloop params in
* the indexqualorig, qpqual, and indexorderbyorig expressions. A bit
* annoying to have to do this separately from the processing in
* fix_indexqual_references --- rethink this when generalizing the inner
* indexscan support. But note we can't really do this earlier because
* it'd break the comparisons to predicates above ... (or would it? Those
* wouldn't have outer refs)
* 我們必須用indexqualorig掉冶、qpqual和indexorderbyorig表達(dá)式中的nestloop參數(shù)替換任何外關(guān)系變量真竖。
* 與fix_indexqual_references中的處理分開來完成這一工作有點(diǎn)煩人——在泛化內(nèi)部indexscan支持時(shí)重新考慮這一點(diǎn)。
* 但是請(qǐng)注意厌小,不能提前做這個(gè)事情恢共,因?yàn)樗鼤?huì)打斷與上面謂詞的比較……(可能發(fā)生嗎?因?yàn)樗鼈儧]有外部依賴)
*/
if (best_path->path.param_info)//存在參數(shù)信息,使用內(nèi)嵌循環(huán)參數(shù)替換
{
stripped_indexquals = (List *)
replace_nestloop_params(root, (Node *) stripped_indexquals);
qpqual = (List *)
replace_nestloop_params(root, (Node *) qpqual);
indexorderbys = (List *)
replace_nestloop_params(root, (Node *) indexorderbys);
}
/*
* If there are ORDER BY expressions, look up the sort operators for their
* result datatypes.
* //存在ORDER BY表達(dá)式
*/
if (indexorderbys)
{
ListCell *pathkeyCell,
*exprCell;
/*
* PathKey contains OID of the btree opfamily we're sorting by, but
* that's not quite enough because we need the expression's datatype
* to look up the sort operator in the operator family.
* PathKey已經(jīng)包含我們正在排序的btree opfamily的OID,
* 但這還不足夠召锈,因?yàn)樾枰磉_(dá)式的數(shù)據(jù)類型來查找操作符家族中的sort操作符旁振。
*/
Assert(list_length(best_path->path.pathkeys) == list_length(indexorderbys));
forboth(pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys)
{
PathKey *pathkey = (PathKey *) lfirst(pathkeyCell);
Node *expr = (Node *) lfirst(exprCell);
Oid exprtype = exprType(expr);
Oid sortop;
/* Get sort operator from opfamily */
sortop = get_opfamily_member(pathkey->pk_opfamily,
exprtype,
exprtype,
pathkey->pk_strategy);
if (!OidIsValid(sortop))
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
pathkey->pk_strategy, exprtype, exprtype, pathkey->pk_opfamily);
indexorderbyops = lappend_oid(indexorderbyops, sortop);
}
}
/* Finally ready to build the plan node */
//OK,下面可以構(gòu)建計(jì)劃節(jié)點(diǎn)了
if (indexonly)
scan_plan = (Scan *) make_indexonlyscan(tlist,
qpqual,
baserelid,
indexoid,
fixed_indexquals,
fixed_indexorderbys,
best_path->indexinfo->indextlist,
best_path->indexscandir);
else
scan_plan = (Scan *) make_indexscan(tlist,
qpqual,
baserelid,
indexoid,
fixed_indexquals,
stripped_indexquals,
fixed_indexorderbys,
indexorderbys,
indexorderbyops,
best_path->indexscandir);
copy_generic_path_info(&scan_plan->plan, &best_path->path);
return scan_plan;
}
//------------------------------------- make_indexscan/make_indexonlyscan
static IndexScan *
make_indexscan(List *qptlist,
List *qpqual,
Index scanrelid,
Oid indexid,
List *indexqual,
List *indexqualorig,
List *indexorderby,
List *indexorderbyorig,
List *indexorderbyops,
ScanDirection indexscandir)
{
IndexScan *node = makeNode(IndexScan);
Plan *plan = &node->scan.plan;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->indexid = indexid;
node->indexqual = indexqual;
node->indexqualorig = indexqualorig;
node->indexorderby = indexorderby;
node->indexorderbyorig = indexorderbyorig;
node->indexorderbyops = indexorderbyops;
node->indexorderdir = indexscandir;
return node;
}
static IndexOnlyScan *
make_indexonlyscan(List *qptlist,
List *qpqual,
Index scanrelid,
Oid indexid,
List *indexqual,
List *indexorderby,
List *indextlist,
ScanDirection indexscandir)
{
IndexOnlyScan *node = makeNode(IndexOnlyScan);
Plan *plan = &node->scan.plan;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->indexid = indexid;
node->indexqual = indexqual;
node->indexorderby = indexorderby;
node->indextlist = indextlist;
node->indexorderdir = indexscandir;
return node;
}
三、跟蹤分析
測(cè)試腳本如下
testdb=# explain select dw.*,grjf.grbh,grjf.xm,grjf.ny,grjf.je
testdb-# from t_dwxx dw,lateral (select gr.grbh,gr.xm,jf.ny,jf.je
testdb(# from t_grxx gr inner join t_jfxx jf
testdb(# on gr.dwbh = dw.dwbh
testdb(# and gr.grbh = jf.grbh) grjf
testdb-# where dw.dwbh in ('1001','1002')
testdb-# order by dw.dwbh;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Sort (cost=2010.12..2010.17 rows=20 width=47)
Sort Key: dw.dwbh
-> Nested Loop (cost=14.24..2009.69 rows=20 width=47)
-> Hash Join (cost=13.95..2002.56 rows=20 width=32)
Hash Cond: ((gr.dwbh)::text = (dw.dwbh)::text)
-> Seq Scan on t_grxx gr (cost=0.00..1726.00 rows=100000 width=16)
-> Hash (cost=13.92..13.92 rows=2 width=20)
-> Index Scan using t_dwxx_pkey on t_dwxx dw (cost=0.29..13.92 rows=2 width=20)
Index Cond: ((dwbh)::text = ANY ('{1001,1002}'::text[]))
-> Index Scan using idx_t_jfxx_grbh on t_jfxx jf (cost=0.29..0.35 rows=1 width=20)
Index Cond: ((grbh)::text = (gr.grbh)::text)
(11 rows)
啟動(dòng)gdb,設(shè)置斷點(diǎn),進(jìn)入函數(shù)create_scan_plan
(gdb) b create_scan_plan
Breakpoint 1 at 0x7b7b78: file createplan.c, line 514.
(gdb) c
Continuing.
Breakpoint 1, create_scan_plan (root=0x2d36d00, best_path=0x2d57ad0, flags=0) at createplan.c:514
514 RelOptInfo *rel = best_path->parent;
pathtype為T_SeqScan
(gdb) p best_path->pathtype
$1 = T_SeqScan
進(jìn)入相應(yīng)的分支,約束條件直接使用Relation的限制條件
(gdb) n
532 switch (best_path->pathtype)
(gdb)
539 scan_clauses = rel->baserestrictinfo;
(gdb)
540 break;
非索引快速掃描,構(gòu)建目標(biāo)投影列
(gdb) n
559 if (gating_clauses)
(gdb)
572 if (flags == CP_IGNORE_TLIST)
(gdb)
576 else if (use_physical_tlist(root, best_path, flags))
(gdb)
578 if (best_path->pathtype == T_IndexOnlyScan)
(gdb)
592 tlist = build_physical_tlist(root, rel);
(gdb)
593 if (tlist == NIL)
(gdb)
601 if (flags & CP_LABEL_TLIST)
(gdb)
(gdb) p *tlist
$4 = {type = T_List, length = 5, head = 0x2d89440, tail = 0x2d897d8}
(gdb) p *(Node *)tlist->head->data.ptr_value
$5 = {type = T_TargetEntry}
(gdb) p *(TargetEntry *)tlist->head->data.ptr_value
$6 = {xpr = {type = T_TargetEntry}, expr = 0x2d89390, resno = 1, resname = 0x0, ressortgroupref = 0, resorigtbl = 0,
resorigcol = 0, resjunk = false}
根據(jù)pathtype進(jìn)入相應(yīng)的處理邏輯,調(diào)用函數(shù)create_seqscan_plan
(gdb) n
614 plan = (Plan *) create_seqscan_plan(root,
(gdb) step
create_seqscan_plan (root=0x2d36d00, best_path=0x2d57ad0, tlist=0x2d89468, scan_clauses=0x0) at createplan.c:2444
2444 Index scan_relid = best_path->parent->relid;
構(gòu)建掃描條件,為NULL
2451 scan_clauses = order_qual_clauses(root, scan_clauses);
(gdb)
2454 scan_clauses = extract_actual_clauses(scan_clauses, false);
(gdb)
2457 if (best_path->param_info)
(gdb)
(gdb) p *scan_clauses
Cannot access memory at address 0x0
生成SeqScan Plan節(jié)點(diǎn),并把啟動(dòng)成本/總成本等相關(guān)信息拷貝到該P(yáng)lan中
(gdb) n
2463 scan_plan = make_seqscan(tlist,
(gdb)
2467 copy_generic_path_info(&scan_plan->plan, best_path);
完成創(chuàng)建,返回Plan
(gdb) n
2469 return scan_plan;
(gdb) p *scan_plan
$1 = {plan = {type = T_SeqScan, startup_cost = 0, total_cost = 1726, plan_rows = 100000, plan_width = 16,
parallel_aware = false, parallel_safe = true, plan_node_id = 0, targetlist = 0x2db4e38, qual = 0x0, lefttree = 0x0,
righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, scanrelid = 3}
下面跟蹤分析create_indexscan_plan函數(shù)
設(shè)置斷點(diǎn),進(jìn)入函數(shù)
(gdb) b create_indexscan_plan
Breakpoint 2 at 0x7badad: file createplan.c, line 2536.
(gdb) c
Continuing.
Breakpoint 1, create_scan_plan (root=0x2d50a00, best_path=0x2da9e98, flags=2) at createplan.c:514
514 RelOptInfo *rel = best_path->parent;
(gdb) c
Continuing.
Breakpoint 2, create_indexscan_plan (root=0x2d50a00, best_path=0x2da9e98, tlist=0x2db5250, scan_clauses=0x2da8998,
indexonly=false) at createplan.c:2536
2536 List *indexquals = best_path->indexquals;
(gdb)
賦值并獲取相關(guān)信息
2536 List *indexquals = best_path->indexquals;
(gdb) n
2537 List *indexorderbys = best_path->indexorderbys;
(gdb)
2538 Index baserelid = best_path->path.parent->relid;
(gdb)
2539 Oid indexoid = best_path->indexinfo->indexoid;
(gdb)
2544 List *indexorderbyops = NIL;
(gdb)
2548 Assert(baserelid > 0);
(gdb)
2549 Assert(best_path->path.parent->rtekind == RTE_RELATION);
(gdb)
2555 stripped_indexquals = get_actual_clauses(indexquals);
(gdb) n
2561 fixed_indexquals = fix_indexqual_references(root, best_path);
(gdb)
2566 fixed_indexorderbys = fix_indexorderby_references(root, best_path);
(gdb)
2596 qpqual = NIL;
(gdb)
(gdb) p baserelid
$3 = 1
(gdb) p indexoid
$4 = 16738
#t_dwxx_pkey
testdb=# select relname from pg_class where oid=16738;
relname
-------------
t_dwxx_pkey
(1 row)
遍歷掃描條件
2597 foreach(l, scan_clauses)
(gdb) p *scan_clauses
$5 = {type = T_List, length = 1, head = 0x2da8970, tail = 0x2da8970}
重復(fù)的約束條件,不需要處理
2603 if (list_member_ptr(indexquals, rinfo))
(gdb)
2604 continue; /* simple duplicate */
對(duì)條件進(jìn)行排序&規(guī)約
2614 qpqual = order_qual_clauses(root, qpqual);
(gdb) n
2617 qpqual = extract_actual_clauses(qpqual, false);
(gdb) n
2628 if (best_path->path.param_info)
(gdb) p *qpqual
Cannot access memory at address 0x0
創(chuàng)建IndexScan節(jié)點(diǎn)
(gdb) n
2642 if (indexorderbys)
(gdb)
2673 if (indexonly)
(gdb)
2683 scan_plan = (Scan *) make_indexscan(tlist,
(gdb)
2694 copy_generic_path_info(&scan_plan->plan, &best_path->path);
(gdb)
2696 return scan_plan;
(gdb)
2697 }
(gdb) p *scan_plan
$6 = {plan = {type = T_IndexScan, startup_cost = 0.28500000000000003, total_cost = 13.924222117799655, plan_rows = 2,
plan_width = 20, parallel_aware = false, parallel_safe = true, plan_node_id = 0, targetlist = 0x2db5250, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, scanrelid = 1}
(gdb)
回到create_scan_plan
(gdb) n
create_scan_plan (root=0x2d50a00, best_path=0x2da9e98, flags=2) at createplan.c:633
633 break;
(gdb)
732 if (gating_clauses)
(gdb)
735 return plan;
(gdb)
736 }
調(diào)用完成.