PostgreSQL 原始碼解讀(73)- 查詢語句#58(grouping_planner函式...
先前花了二十多個小節介紹query_planner及其子函式make_one_rel,已基本介紹完畢。本節回過頭來Review query_planner函式的呼叫方-查詢最佳化實現中的grouping_planner函式,該函式執行與與分組/聚集相關的"規劃步驟"。
一、原始碼解讀
分組/聚集等操作是在一個Relation上疊加分組/聚集運算,grouping_planner函式首先透過query_planner函式生成一個新的關係,然後在此關係上attached分組/聚集等操作。
/*--------------------
* grouping_planner
* Perform planning steps related to grouping, aggregation, etc.
* 執行與與分組/聚集相關的"規劃步驟".
* 分組/聚集等操作是在一個Relation上疊加分組/聚集運算,
* PG首先透過query_planner函式生成一個新的關係,然後在此關係上attached分組/聚集等操作
*
* This function adds all required top-level processing to the scan/join
* Path(s) produced by query_planner.
*
* 該函式還處理了所有需要在頂層處理的掃描/連線路徑(透過query_planner函式生成)
*
* If inheritance_update is true, we're being called from inheritance_planner
* and should not include a ModifyTable step in the resulting Path(s).
* (inheritance_planner will create a single ModifyTable node covering all the
* target tables.)
*
* 如果標誌inheritance_update為true,這個函式的呼叫者是inheritance_planner,在結果路徑中
* 不應包含ModifyTable步驟(inheritance_planner會建立一個單獨的覆蓋所有目標表的ModifyTable節點).
*
* tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as follows:
* 0: expect all tuples to be retrieved (normal case)
* 0 < tuple_fraction < 1: expect the given fraction of tuples available
* from the plan to be retrieved
* tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
* expected to be retrieved (ie, a LIMIT specification)
*
* tuple_fraction是我們希望搜尋的元組比例:
* 0:正常情況下,期望掃描所有的元組
* 大於0小於1:按給定的比例掃描
* 大於等於1:掃描的元組數量(比如透過LIMIT語句指定)
*
* Returns nothing; the useful output is in the Paths we attach to the
* (UPPERREL_FINAL, NULL) upperrel in *root. In addition,
* root->processed_tlist contains the final processed targetlist.
*
* 該函式沒有返回值,有用的輸出是root->upperrel->Paths,另外,root->processed_tlist中儲存最終的投影列
*
* Note that we have not done set_cheapest() on the final rel; it's convenient
* to leave this to the caller.
*--------------------
*/
static void
grouping_planner(PlannerInfo *root, bool inheritance_update,
double tuple_fraction)
{
Query *parse = root->parse;
List *tlist;
int64 offset_est = 0;
int64 count_est = 0;
double limit_tuples = -1.0;
bool have_postponed_srfs = false;
PathTarget *final_target;
List *final_targets;
List *final_targets_contain_srfs;
bool final_target_parallel_safe;
RelOptInfo *current_rel;
RelOptInfo *final_rel;
ListCell *lc;
/* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */
//如果存在LIMIT/OFFSET子句,調整tuple_fraction
if (parse->limitCount || parse->limitOffset)//存在LIMIT/OFFSET語句
{
tuple_fraction = preprocess_limit(root, tuple_fraction,
&offset_est, &count_est);//獲取元組數量
/*
* If we have a known LIMIT, and don't have an unknown OFFSET, we can
* estimate the effects of using a bounded sort.
* 如果我們有一個已知LIMIT,並且沒有未知的OFFSET,我們可以估算使用有界排序的效果。
*/
if (count_est > 0 && offset_est >= 0)
limit_tuples = (double) count_est + (double) offset_est;//
}
/* Make tuple_fraction accessible to lower-level routines */
//使tuple_fraction可被低階別的處理過程訪問(在最佳化器資訊中設定)
root->tuple_fraction = tuple_fraction;//設定值
if (parse->setOperations)//集合操作,如UNION等
{
/*
* If there's a top-level ORDER BY, assume we have to fetch all the
* tuples. This might be too simplistic given all the hackery below
* to possibly avoid the sort; but the odds of accurate estimates here
* are pretty low anyway. XXX try to get rid of this in favor of
* letting plan_set_operations generate both fast-start and
* cheapest-total paths.
* 如果語句的最外層(頂級)存在ORDER BY子句,假設我們必須獲取所有元組。
* 這可能過於簡單,但無論如何,準確估計的機率是相當低的。
* XXX試圖擺脫這種情況,讓plan_set_operations同時生成快速啟動和最便宜的路徑。
*/
if (parse->sortClause)
root->tuple_fraction = 0.0;//存在排序操作,需掃描所有的元組
/*
* Construct Paths for set operations. The results will not need any
* work except perhaps a top-level sort and/or LIMIT. Note that any
* special work for recursive unions is the responsibility of
* plan_set_operations.
* 為集合操作構造路徑。
* 除了最外層的SORT/LIMIT操作外不需要作其他操作。
注意,遞迴聯合的任何特殊工作都是plan_set_operations負責。
*/
current_rel = plan_set_operations(root);//呼叫集合操作的"規劃"函式
/*
* We should not need to call preprocess_targetlist, since we must be
* in a SELECT query node. Instead, use the targetlist returned by
* plan_set_operations (since this tells whether it returned any
* resjunk columns!), and transfer any sort key information from the
* original tlist.
* 我們不需要呼叫preprocess_targetlist函式,因為執行這些操作必須在SELECT查詢NODE中。
* 相反,使用plan_set_operations函式返回的targetlist(因為這告訴它是否返回了所有的resjunk列),
* 並從原始投影列連結串列tlist中傳輸所有的排序sort鍵資訊。
*/
Assert(parse->commandType == CMD_SELECT);
tlist = root->processed_tlist; /* 從plan_set_operations函式的返回結果中獲取;from plan_set_operations */
/* for safety, copy processed_tlist instead of modifying in-place */
//為了安全起見,複製processed_tlist,而不是就地修改
tlist = postprocess_setop_tlist(copyObject(tlist), parse->targetList);
/* Save aside the final decorated tlist */
//
root->processed_tlist = tlist;
/* Also extract the PathTarget form of the setop result tlist */
//從集合操作結果投影列中獲取PathTarget格式的結果列
final_target = current_rel->cheapest_total_path->pathtarget;
/* And check whether it's parallel safe */
//檢查是否並行安全
final_target_parallel_safe =
is_parallel_safe(root, (Node *) final_target->exprs);
/* The setop result tlist couldn't contain any SRFs */
//集合操作結果投影列不能包含任何的SRFs
Assert(!parse->hasTargetSRFs);
final_targets = final_targets_contain_srfs = NIL;
/*
* Can't handle FOR [KEY] UPDATE/SHARE here (parser should have
* checked already, but let's make sure).
* 無法在這裡處理[KEY]更新/共享(解析器應該已經檢查過了,但需要確認)。
*/
if (parse->rowMarks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------
translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT",
LCS_asString(linitial_node(RowMarkClause,
parse->rowMarks)->strength))));
/*
* Calculate pathkeys that represent result ordering requirements
* 計算表示結果排序需求的pathkeys
*/
Assert(parse->distinctClause == NIL);
root->sort_pathkeys = make_pathkeys_for_sortclauses(root,
parse->sortClause,
tlist);
}
else//非集合操作
{
/* No set operations, do regular planning */
//沒有集合操作,執行常規的規劃過程
PathTarget *sort_input_target;
List *sort_input_targets;
List *sort_input_targets_contain_srfs;
bool sort_input_target_parallel_safe;
PathTarget *grouping_target;
List *grouping_targets;
List *grouping_targets_contain_srfs;
bool grouping_target_parallel_safe;
PathTarget *scanjoin_target;
List *scanjoin_targets;
List *scanjoin_targets_contain_srfs;
bool scanjoin_target_parallel_safe;
bool scanjoin_target_same_exprs;
bool have_grouping;
AggClauseCosts agg_costs;
WindowFuncLists *wflists = NULL;
List *activeWindows = NIL;
grouping_sets_data *gset_data = NULL;
standard_qp_extra qp_extra;
/* A recursive query should always have setOperations */
//遞迴查詢應包含集合操作,檢查!
Assert(!root->hasRecursion);//檢查
/* Preprocess grouping sets and GROUP BY clause, if any */
//預處理grouping sets語句和GROUP BY 子句
if (parse->groupingSets)//
{
gset_data = preprocess_grouping_sets(root);//預處理grouping sets語句
}
else
{
/* Preprocess regular GROUP BY clause, if any */
//如處理常規的GROUP BY 子句
if (parse->groupClause)
parse->groupClause = preprocess_groupclause(root, NIL);//處理普通的Group By語句
}
/* Preprocess targetlist */
//預處理投影列
tlist = preprocess_targetlist(root);//處理投影列
/*
* We are now done hacking up the query's targetlist. Most of the
* remaining planning work will be done with the PathTarget
* representation of tlists, but save aside the full representation so
* that we can transfer its decoration (resnames etc) to the topmost
* tlist of the finished Plan.
* 現在已經完成了對查詢語句targetlist的hacking工作。
* 剩下的大部分規劃工作將使用tlists的PathTarget來完成,
* 但是需要保留完整的資訊,這樣我們就可以將它的修飾資訊(如resname等)轉移到完成計劃的最頂層tlist中。
*/
root->processed_tlist = tlist;//賦值
/*
* Collect statistics about aggregates for estimating costs, and mark
* all the aggregates with resolved aggtranstypes. We must do this
* before slicing and dicing the tlist into various pathtargets, else
* some copies of the Aggref nodes might escape being marked with the
* correct transtypes.
* 收集關於聚集操作的統計資料以估計成本,並在所有聚集操作上標上已解決的aggtranstypes。
* 必須在將tlist切割成各種PathKeys之前完成這項工作,
* 否則一些Aggref節點的副本中正確transtypes可能會被替換。
*
* Note: currently, we do not detect duplicate aggregates here. This
* may result in somewhat-overestimated cost, which is fine for our
* purposes since all Paths will get charged the same. But at some
* point we might wish to do that detection in the planner, rather
* than during executor startup.
* 注意:目前,我們沒有檢測到重複的聚合。
* 這可能會導致一些過高估算的成本,這對於我們的目的來說是好的,因為所有的Path都會耗費相同的成本。
* 但在某些時候,可能希望在計劃器中進行檢測,而不是在執行器executor啟動期間。
*/
MemSet(&agg_costs, 0, sizeof(AggClauseCosts));
if (parse->hasAggs)//存在聚合函式
{
get_agg_clause_costs(root, (Node *) tlist, AGGSPLIT_SIMPLE,
&agg_costs);//收集用於估算成本的統計資訊
get_agg_clause_costs(root, parse->havingQual, AGGSPLIT_SIMPLE,
&agg_costs);//收集用於估算成本的統計資訊
}
/*
* Locate any window functions in the tlist. (We don't need to look
* anywhere else, since expressions used in ORDER BY will be in there
* too.) Note that they could all have been eliminated by constant
* folding, in which case we don't need to do any more work.
* 在tlist中找到所有的視窗函式。
* (我們不需要在其他地方查詢,因為ORDER BY中使用的表示式也在那裡。)
* 注意,它們可以透過不斷摺疊來消除,在這種情況下,我們不需要做更多的工作。
*/
if (parse->hasWindowFuncs)//視窗函式
{
wflists = find_window_functions((Node *) tlist,
list_length(parse->windowClause));
if (wflists->numWindowFuncs > 0)
activeWindows = select_active_windows(root, wflists);
else
parse->hasWindowFuncs = false;
}
/*
* Preprocess MIN/MAX aggregates, if any. Note: be careful about
* adding logic between here and the query_planner() call. Anything
* that is needed in MIN/MAX-optimizable cases will have to be
* duplicated in planagg.c.
* 重新處理MAX/MIN聚集操作,如果有的話。
* 注意:在這裡和query_planner()呼叫之間新增邏輯時要小心。
* 在MIN/MAX最佳化情況下需要的所有東西都必須在plan .c中重複。
*/
if (parse->hasAggs)//預處理最大最小聚合
preprocess_minmax_aggregates(root, tlist);
/*
* Figure out whether there's a hard limit on the number of rows that
* query_planner's result subplan needs to return. Even if we know a
* hard limit overall, it doesn't apply if the query has any
* grouping/aggregation operations, or SRFs in the tlist.
* 計算query_planner結果子計劃需要返回的行數是否有硬性限制。
* 即使我們知道總的強制限制,如果查詢在tlist中有任何分組/聚合操作或SRFs,它也不適用。
*/
if (parse->groupClause ||
parse->groupingSets ||
parse->distinctClause ||
parse->hasAggs ||
parse->hasWindowFuncs ||
parse->hasTargetSRFs ||
root->hasHavingQual)//存在Group By/Grouping Set等語句,則limit_tuples設定為-1
root->limit_tuples = -1.0;
else
root->limit_tuples = limit_tuples;//否則,正常賦值
/* Set up data needed by standard_qp_callback */
//配置standard_qp_callback函式需要的相關資料
qp_extra.tlist = tlist;//賦值
qp_extra.activeWindows = activeWindows;
qp_extra.groupClause = (gset_data
? (gset_data->rollups ? linitial_node(RollupData, gset_data->rollups)->groupClause : NIL)
: parse->groupClause);
/*
* Generate the best unsorted and presorted paths for the scan/join
* portion of this Query, ie the processing represented by the
* FROM/WHERE clauses. (Note there may not be any presorted paths.)
* We also generate (in standard_qp_callback) pathkey representations
* of the query's sort clause, distinct clause, etc.
* 為這個查詢的掃描/連線部分(即FROM/WHERE子句表示的處理)生成最好的未排序和預排序路徑。
* (注意,可能沒有任何預先設定的路徑。)
* 我們還生成(在standard_qp_callback中)查詢語句的sort子句和distinct子句對應的PathKey。
*/
//為查詢中的掃描/連線部分生成最優的未排序/預排序路徑(如FROM/WHERE語句表示的處理過程)
current_rel = query_planner(root, tlist,
standard_qp_callback, &qp_extra);
/*
* Convert the query's result tlist into PathTarget format.
* 轉換查詢結果為PathTarget格式
*
* Note: it's desirable to not do this till after query_planner(),
* because the target width estimates can use per-Var width numbers
* that were obtained within query_planner().
* 注意:在query_planner()之後才需要這樣做,因為目標列的寬度估算可以使用在query_planner()中獲得的每個VAR資訊。
*/
final_target = create_pathtarget(root, tlist);
final_target_parallel_safe =
is_parallel_safe(root, (Node *) final_target->exprs);
/*
* If ORDER BY was given, consider whether we should use a post-sort
* projection, and compute the adjusted target for preceding steps if
* so.
* 如果存在ORDER BY子句,考慮是否使用post-sort投影,如使用則計算前面已調整過的步驟目標列。
*/
if (parse->sortClause)//存在sort語句?
{
sort_input_target = make_sort_input_target(root,
final_target,
&have_postponed_srfs);
sort_input_target_parallel_safe =
is_parallel_safe(root, (Node *) sort_input_target->exprs);
}
else
{
sort_input_target = final_target;//不存在,則直接賦值
sort_input_target_parallel_safe = final_target_parallel_safe;
}
/*
* If we have window functions to deal with, the output from any
* grouping step needs to be what the window functions want;
* otherwise, it should be sort_input_target.
* 如果要處理視窗函式,任何分組步驟的輸出都需要滿足視窗函式的要求;
* 否則,它應該是sort_input_target。
*/
if (activeWindows)//存在視窗函式?
{
grouping_target = make_window_input_target(root,
final_target,
activeWindows);
grouping_target_parallel_safe =
is_parallel_safe(root, (Node *) grouping_target->exprs);
}
else
{
grouping_target = sort_input_target;
grouping_target_parallel_safe = sort_input_target_parallel_safe;
}
/*
* If we have grouping or aggregation to do, the topmost scan/join
* plan node must emit what the grouping step wants; otherwise, it
* should emit grouping_target.
* 如果要進行分組或聚合,最外層的掃描/連線計劃節點必須發出分組步驟需要的內容;
* 否則,它應該設定grouping_target。
*/
have_grouping = (parse->groupClause || parse->groupingSets ||
parse->hasAggs || root->hasHavingQual);
if (have_grouping)
{//存在group等分組語句
scanjoin_target = make_group_input_target(root, final_target);
scanjoin_target_parallel_safe =
is_parallel_safe(root, (Node *) grouping_target->exprs);
}
else
{
scanjoin_target = grouping_target;
scanjoin_target_parallel_safe = grouping_target_parallel_safe;
}
/*
* If there are any SRFs in the targetlist, we must separate each of
* these PathTargets into SRF-computing and SRF-free targets. Replace
* each of the named targets with a SRF-free version, and remember the
* list of additional projection steps we need to add afterwards.
* 如果targetlist中有任何SRFs,我們必須將這些PathKeys分別劃分為SRF-computing和SRF-free 目標列。
* 用一個沒有SRF的版本替換每個指定的目標,並記住後面需要新增的其他投影步驟連結串列。
*/
if (parse->hasTargetSRFs)//存在SRFs
{
/* final_target doesn't recompute any SRFs in sort_input_target */
//在sort_input_target中不需要重複計算SRFs
split_pathtarget_at_srfs(root, final_target, sort_input_target,
&final_targets,
&final_targets_contain_srfs);
final_target = linitial_node(PathTarget, final_targets);
Assert(!linitial_int(final_targets_contain_srfs));
/* likewise for sort_input_target vs. grouping_target */
split_pathtarget_at_srfs(root, sort_input_target, grouping_target,
&sort_input_targets,
&sort_input_targets_contain_srfs);
sort_input_target = linitial_node(PathTarget, sort_input_targets);
Assert(!linitial_int(sort_input_targets_contain_srfs));
/* likewise for grouping_target vs. scanjoin_target */
split_pathtarget_at_srfs(root, grouping_target, scanjoin_target,
&grouping_targets,
&grouping_targets_contain_srfs);
grouping_target = linitial_node(PathTarget, grouping_targets);
Assert(!linitial_int(grouping_targets_contain_srfs));
/* scanjoin_target will not have any SRFs precomputed for it */
split_pathtarget_at_srfs(root, scanjoin_target, NULL,
&scanjoin_targets,
&scanjoin_targets_contain_srfs);
scanjoin_target = linitial_node(PathTarget, scanjoin_targets);
Assert(!linitial_int(scanjoin_targets_contain_srfs));
}
else
{
/* initialize lists; for most of these, dummy values are OK */
//初始化連結串列
final_targets = final_targets_contain_srfs = NIL;
sort_input_targets = sort_input_targets_contain_srfs = NIL;
grouping_targets = grouping_targets_contain_srfs = NIL;
scanjoin_targets = list_make1(scanjoin_target);
scanjoin_targets_contain_srfs = NIL;
}
/* Apply scan/join target. */
//應用掃描/連線target
scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1
&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);
apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,
scanjoin_targets_contain_srfs,
scanjoin_target_parallel_safe,
scanjoin_target_same_exprs);
/*
* Save the various upper-rel PathTargets we just computed into
* root->upper_targets[]. The core code doesn't use this, but it
* provides a convenient place for extensions to get at the info. For
* consistency, we save all the intermediate targets, even though some
* of the corresponding upperrels might not be needed for this query.
* 儲存剛剛計算的各種upper- >upper_targets[]資訊。
* 核心程式碼不使用這個功能,但是它為擴充套件提供了一個方便的地方來獲取資訊。
* 為了保持一致性,我們儲存了所有的中間目標列,即使這個查詢可能不需要一些相應的上層關係。
*/
//賦值
root->upper_targets[UPPERREL_FINAL] = final_target;
root->upper_targets[UPPERREL_WINDOW] = sort_input_target;
root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
/*
* If we have grouping and/or aggregation, consider ways to implement
* that. We build a new upperrel representing the output of this
* phase.
* 如果我們有分組和/或聚合,考慮如何實現它。需要構建一個表示此階段輸出的上層關係。
*/
if (have_grouping)//存在分組操作
{
current_rel = create_grouping_paths(root,
current_rel,
grouping_target,
grouping_target_parallel_safe,
&agg_costs,
gset_data);//建立分組訪問路徑
/* Fix things up if grouping_target contains SRFs */
if (parse->hasTargetSRFs)
adjust_paths_for_srfs(root, current_rel,
grouping_targets,
grouping_targets_contain_srfs);
}
/*
* If we have window functions, consider ways to implement those. We
* build a new upperrel representing the output of this phase.
* 如果有視窗函式,考慮如何實現這些函式。
* 我們建立一個新的上層關係表示這個階段的輸出。
*/
if (activeWindows)//存在視窗函式
{
current_rel = create_window_paths(root,
current_rel,
grouping_target,
sort_input_target,
sort_input_target_parallel_safe,
tlist,
wflists,
activeWindows);
/* Fix things up if sort_input_target contains SRFs */
if (parse->hasTargetSRFs)
adjust_paths_for_srfs(root, current_rel,
sort_input_targets,
sort_input_targets_contain_srfs);
}
/*
* If there is a DISTINCT clause, consider ways to implement that. We
* build a new upperrel representing the output of this phase.
* 如果有一個DISTINCT子句,考慮如何實現它。構建一個表示此階段輸出的上層關係。
*/
if (parse->distinctClause)//存在distinct?
{
current_rel = create_distinct_paths(root,
current_rel);
}
} /* end of if (setOperations) */
/*
* If ORDER BY was given, consider ways to implement that, and generate a
* new upperrel containing only paths that emit the correct ordering and
* project the correct final_target. We can apply the original
* limit_tuples limit in sort costing here, but only if there are no
* postponed SRFs.
* 如果指定了ORDER BY,考慮實現它的方法,並生成一個僅包含ORDER和final_target的Path的上層關係。
* 我們可以在排序成本中應用初始的limit_tuples限制,但前提是沒有延遲的SRFs。
*/
if (parse->sortClause)//存在sort語句?
{
current_rel = create_ordered_paths(root,
current_rel,
final_target,
final_target_parallel_safe,
have_postponed_srfs ? -1.0 :
limit_tuples);
/* Fix things up if final_target contains SRFs */
if (parse->hasTargetSRFs)
adjust_paths_for_srfs(root, current_rel,
final_targets,
final_targets_contain_srfs);
}
/*
* Now we are prepared to build the final-output upperrel.
* 可以構建最終的關係了!
*/
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);//獲取最終的RelOptInfo(用於替換RTE)
/*
* If the input rel is marked consider_parallel and there's nothing that's
* not parallel-safe in the LIMIT clause, then the final_rel can be marked
* consider_parallel as well. Note that if the query has rowMarks or is
* not a SELECT, consider_parallel will be false for every relation in the
* query.
* 如果關係被標記為consider_parallel,並且在LIMIT子句中沒有任何非並行安全的地方,
* 那麼final_rel也可以被標記為consider_parallel。
* 請注意,如果查詢有rowMarks或不是SELECT語句,則認為對查詢中的每個關係consider_parallel都為false。
*/
if (current_rel->consider_parallel &&
is_parallel_safe(root, parse->limitOffset) &&
is_parallel_safe(root, parse->limitCount))
final_rel->consider_parallel = true;//並行
/*
* If the current_rel belongs to a single FDW, so does the final_rel.
* 如current_rel屬於某個單獨的FDW,設定final_rel資訊
*/
final_rel->serverid = current_rel->serverid;
final_rel->userid = current_rel->userid;
final_rel->useridiscurrent = current_rel->useridiscurrent;
final_rel->fdwroutine = current_rel->fdwroutine;
/*
* Generate paths for the final_rel. Insert all surviving paths, with
* LockRows, Limit, and/or ModifyTable steps added if needed.
* 為final_rel生成訪問路徑.
* 插入所有篩選後的訪問路徑,包含需新增的LockRows/Limit/ModifyTable步驟
*/
foreach(lc, current_rel->pathlist)//逐一遍歷訪問路徑
{
Path *path = (Path *) lfirst(lc);
/*
* If there is a FOR [KEY] UPDATE/SHARE clause, add the LockRows node.
* (Note: we intentionally test parse->rowMarks not root->rowMarks
* here. If there are only non-locking rowmarks, they should be
* handled by the ModifyTable node instead. However, root->rowMarks
* is what goes into the LockRows node.)
* 如果存在FOR [KEY] UPDATE/SHARE子句,則新增LockRows節點。
* (注意:我們在這裡有意測試的是parse->rowMarks,而不是root->rowMarks。
* 如果只有非鎖定行標記,則應該由ModifyTable節點處理。
* 但是,root->rowMarks是進入LockRows節點的行標記。
*/
if (parse->rowMarks)
{
path = (Path *) create_lockrows_path(root, final_rel, path,
root->rowMarks,
SS_assign_special_param(root));
}
/*
* If there is a LIMIT/OFFSET clause, add the LIMIT node.
* 如果存在LIMIT/OFFSET子句,新增LIMIT節點
*/
if (limit_needed(parse))
{
path = (Path *) create_limit_path(root, final_rel, path,
parse->limitOffset,
parse->limitCount,
offset_est, count_est);
}
/*
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
* inheritance_planner, add the ModifyTable node.
* 如為INSERT/UPDATE/DELETE,而且不是從inheritance_planner函式中呼叫,則新增ModifyTable節點
*/
if (parse->commandType != CMD_SELECT && !inheritance_update)//非查詢語句
{
List *withCheckOptionLists;
List *returningLists;
List *rowMarks;
/*
* Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
* needed.
* 如需要,新增WITH CHECK OPTION and RETURNING資訊
*/
if (parse->withCheckOptions)
withCheckOptionLists = list_make1(parse->withCheckOptions);
else
withCheckOptionLists = NIL;
if (parse->returningList)
returningLists = list_make1(parse->returningList);
else
returningLists = NIL;
/*
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node
* will have dealt with fetching non-locked marked rows, else we
* need to have ModifyTable do that.
* 如果存在FOR [KEY] UPDATE/SHARE子句,那麼LockRows節點將處理獲取非帶鎖標記的行,
* 否則我們需要使用ModifyTable來完成。
*/
if (parse->rowMarks)
rowMarks = NIL;
else
rowMarks = root->rowMarks;
path = (Path *)
create_modifytable_path(root, final_rel,
parse->commandType,
parse->canSetTag,
parse->resultRelation,
NIL,
false,
list_make1_int(parse->resultRelation),
list_make1(path),
list_make1(root),
withCheckOptionLists,
returningLists,
rowMarks,
parse->onConflict,
SS_assign_special_param(root));
}
/* And shove it into final_rel */
//新增到final_rel中
add_path(final_rel, path);
}
/*
* Generate partial paths for final_rel, too,xxwssssssssssssssssss if outer query levels might
* be able to make use of them.
* 並行執行訪問路徑
*/
if (final_rel->consider_parallel && root->query_level > 1 &&
!limit_needed(parse))
{
Assert(!parse->rowMarks && parse->commandType == CMD_SELECT);
foreach(lc, current_rel->partial_pathlist)
{
Path *partial_path = (Path *) lfirst(lc);
add_partial_path(final_rel, partial_path);
}
}
/*
* If there is an FDW that's responsible for all baserels of the query,
* let it consider adding ForeignPaths.
* 如查詢中存在FDW,新增ForeignPaths
*/
if (final_rel->fdwroutine &&
final_rel->fdwroutine->GetForeignUpperPaths)
final_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_FINAL,
current_rel, final_rel,
NULL);
/* Let extensions possibly add some more paths */
//透過擴充套件新增訪問路徑
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_FINAL,
current_rel, final_rel, NULL);
/* Note: currently, we leave it to callers to do set_cheapest() */
//注意:目前的做法是讓呼叫放來執行set_cheap()函式
}
二、參考資料
allpaths.c
cost.h
costsize.c
PG Document:Query Planning
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374818/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PostgreSQL 原始碼解讀(88)- 查詢語句#73(SeqNext函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(83)- 查詢語句#68(PortalStart函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(58)- 查詢語句#43(make_one_rel函式#8-B...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 原始碼解讀(93)- 查詢語句#77(ExecHashJoin函式#3)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原始碼函式
- PostgreSQL 原始碼解讀(38)- 查詢語句#23(query_planner函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(39)- 查詢語句#24(query_planner函式#2)SQL原始碼函式
- PostgreSQL 原始碼解讀(40)- 查詢語句#25(query_planner函式#3)SQL原始碼函式
- PostgreSQL 原始碼解讀(41)- 查詢語句#26(query_planner函式#4)SQL原始碼函式
- PostgreSQL 原始碼解讀(24)- 查詢語句#9(查詢重寫)SQL原始碼
- PostgreSQL 原始碼解讀(70)- 查詢語句#55(make_one_rel函式#20-...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 原始碼解讀(79)- 查詢語句#64(create_plan函式#3-Se...SQL原始碼函式
- PostgreSQL 原始碼解讀(80)- 查詢語句#65(create_plan函式#4-Jo...SQL原始碼函式
- PostgreSQL 原始碼解讀(78)- 查詢語句#63(create_plan函式#2-cr...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 原始碼解讀(20)- 查詢語句#5(查詢樹Query詳解)SQL原始碼
- PostgreSQL 原始碼解讀(18)- 查詢語句#3(SQL Parse)SQL原始碼