PostgreSQL 原始碼解讀(85)- 查詢語句#70(PortalRun->InitPla...
本節介紹了PortalStart->ExecutorStart(standard_ExecutorStart)->InitPlan->ExecInitNode函式的實現邏輯,該函式透過遞迴呼叫初始化計劃樹中的所有Plan節點。
一、資料結構
Plan
所有計劃節點透過將Plan結構作為第一個欄位從Plan結構“派生”。這確保了在將節點轉換為計劃節點時,一切都能正常工作。(在執行器中以通用方式傳遞時,節點指標經常被轉換為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)
* 所有計劃節點透過將Plan結構作為第一個欄位從Plan結構“派生”。
* 這確保了在將節點轉換為計劃節點時,一切都能正常工作。
* (在執行器中以通用方式傳遞時,節點指標經常被轉換為Plan *)
*
* We never actually instantiate any Plan nodes; this is just the common
* abstract superclass for all Plan-type nodes.
* 從未例項化任何Plan節點;這只是所有Plan-type節點的通用抽象超類。
* ----------------
*/
typedef struct Plan
{
NodeTag type;//節點型別
/*
* 成本估算資訊;estimated execution costs for plan (see costsize.c for more info)
*/
Cost startup_cost; /* 啟動成本;cost expended before fetching any tuples */
Cost total_cost; /* 總成本;total cost (assuming all tuples fetched) */
/*
* 最佳化器估算資訊;planner's estimate of result size of this plan step
*/
double plan_rows; /* 行數;number of rows plan is expected to emit */
int plan_width; /* 平均行大小(Byte為單位);average row width in bytes */
/*
* 並行執行相關的資訊;information needed for parallel query
*/
bool parallel_aware; /* 是否參與並行執行邏輯?engage parallel-aware logic? */
bool parallel_safe; /* 是否並行安全;OK to use as part of parallel plan? */
/*
* Plan型別節點通用的資訊.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;
二、原始碼解讀
ExecInitNode函式遞迴初始化計劃樹中的所有Plan節點,返回對應給定的Plan Node節點的PlanState節點.
/* ------------------------------------------------------------------------
* ExecInitNode
*
* Recursively initializes all the nodes in the plan tree rooted
* at 'node'.
* 遞迴初始化計劃樹中的所有Plan節點.
*
* Inputs:
* 'node' is the current node of the plan produced by the query planner
* 'estate' is the shared execution state for the plan tree
* 'eflags' is a bitwise OR of flag bits described in executor.h
* node-查詢計劃器產生的當前節點
* estate-Plan樹共享的執行狀態資訊
* eflags-一個位或標記位,在executor.h中描述
*
* Returns a PlanState node corresponding to the given Plan node.
* 返回對應Plan Node節點的PlanState節點
* ------------------------------------------------------------------------
*/
PlanState *
ExecInitNode(Plan *node, EState *estate, int eflags)
{
PlanState *result;//結果
List *subps;//子PlanState連結串列
ListCell *l;//臨時變數
/*
* do nothing when we get to the end of a leaf on tree.
* 如node為NULL則返回NULL
*/
if (node == NULL)
return NULL;
/*
* Make sure there's enough stack available. Need to check here, in
* addition to ExecProcNode() (via ExecProcNodeFirst()), to ensure the
* stack isn't overrun while initializing the node tree.
* 確保有足夠的堆疊可用。
* 除了ExecProcNode()(透過ExecProcNodeFirst()呼叫),還需要在這裡進行檢查,以確保在初始化節點樹時堆疊沒有溢位。
*/
check_stack_depth();
switch (nodeTag(node))//根據節點型別進入相應的邏輯
{
/*
* 控制節點;control nodes
*/
case T_Result:
result = (PlanState *) ExecInitResult((Result *) node,
estate, eflags);
break;
case T_ProjectSet:
result = (PlanState *) ExecInitProjectSet((ProjectSet *) node,
estate, eflags);
break;
case T_ModifyTable:
result = (PlanState *) ExecInitModifyTable((ModifyTable *) node,
estate, eflags);
break;
case T_Append:
result = (PlanState *) ExecInitAppend((Append *) node,
estate, eflags);
break;
case T_MergeAppend:
result = (PlanState *) ExecInitMergeAppend((MergeAppend *) node,
estate, eflags);
break;
case T_RecursiveUnion:
result = (PlanState *) ExecInitRecursiveUnion((RecursiveUnion *) node,
estate, eflags);
break;
case T_BitmapAnd:
result = (PlanState *) ExecInitBitmapAnd((BitmapAnd *) node,
estate, eflags);
break;
case T_BitmapOr:
result = (PlanState *) ExecInitBitmapOr((BitmapOr *) node,
estate, eflags);
break;
/*
* 掃描節點;scan nodes
*/
case T_SeqScan:
result = (PlanState *) ExecInitSeqScan((SeqScan *) node,
estate, eflags);
break;
case T_SampleScan:
result = (PlanState *) ExecInitSampleScan((SampleScan *) node,
estate, eflags);
break;
case T_IndexScan:
result = (PlanState *) ExecInitIndexScan((IndexScan *) node,
estate, eflags);
break;
case T_IndexOnlyScan:
result = (PlanState *) ExecInitIndexOnlyScan((IndexOnlyScan *) node,
estate, eflags);
break;
case T_BitmapIndexScan:
result = (PlanState *) ExecInitBitmapIndexScan((BitmapIndexScan *) node,
estate, eflags);
break;
case T_BitmapHeapScan:
result = (PlanState *) ExecInitBitmapHeapScan((BitmapHeapScan *) node,
estate, eflags);
break;
case T_TidScan:
result = (PlanState *) ExecInitTidScan((TidScan *) node,
estate, eflags);
break;
case T_SubqueryScan:
result = (PlanState *) ExecInitSubqueryScan((SubqueryScan *) node,
estate, eflags);
break;
case T_FunctionScan:
result = (PlanState *) ExecInitFunctionScan((FunctionScan *) node,
estate, eflags);
break;
case T_TableFuncScan:
result = (PlanState *) ExecInitTableFuncScan((TableFuncScan *) node,
estate, eflags);
break;
case T_ValuesScan:
result = (PlanState *) ExecInitValuesScan((ValuesScan *) node,
estate, eflags);
break;
case T_CteScan:
result = (PlanState *) ExecInitCteScan((CteScan *) node,
estate, eflags);
break;
case T_NamedTuplestoreScan:
result = (PlanState *) ExecInitNamedTuplestoreScan((NamedTuplestoreScan *) node,
estate, eflags);
break;
case T_WorkTableScan:
result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
estate, eflags);
break;
case T_ForeignScan:
result = (PlanState *) ExecInitForeignScan((ForeignScan *) node,
estate, eflags);
break;
case T_CustomScan:
result = (PlanState *) ExecInitCustomScan((CustomScan *) node,
estate, eflags);
break;
/*
* 連線節點/join nodes
*/
case T_NestLoop:
result = (PlanState *) ExecInitNestLoop((NestLoop *) node,
estate, eflags);
break;
case T_MergeJoin:
result = (PlanState *) ExecInitMergeJoin((MergeJoin *) node,
estate, eflags);
break;
case T_HashJoin:
result = (PlanState *) ExecInitHashJoin((HashJoin *) node,
estate, eflags);
break;
/*
* 物化節點/materialization nodes
*/
case T_Material:
result = (PlanState *) ExecInitMaterial((Material *) node,
estate, eflags);
break;
case T_Sort:
result = (PlanState *) ExecInitSort((Sort *) node,
estate, eflags);
break;
case T_Group:
result = (PlanState *) ExecInitGroup((Group *) node,
estate, eflags);
break;
case T_Agg:
result = (PlanState *) ExecInitAgg((Agg *) node,
estate, eflags);
break;
case T_WindowAgg:
result = (PlanState *) ExecInitWindowAgg((WindowAgg *) node,
estate, eflags);
break;
case T_Unique:
result = (PlanState *) ExecInitUnique((Unique *) node,
estate, eflags);
break;
case T_Gather:
result = (PlanState *) ExecInitGather((Gather *) node,
estate, eflags);
break;
case T_GatherMerge:
result = (PlanState *) ExecInitGatherMerge((GatherMerge *) node,
estate, eflags);
break;
case T_Hash:
result = (PlanState *) ExecInitHash((Hash *) node,
estate, eflags);
break;
case T_SetOp:
result = (PlanState *) ExecInitSetOp((SetOp *) node,
estate, eflags);
break;
case T_LockRows:
result = (PlanState *) ExecInitLockRows((LockRows *) node,
estate, eflags);
break;
case T_Limit:
result = (PlanState *) ExecInitLimit((Limit *) node,
estate, eflags);
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
result = NULL; /* 避免最佳化器提示警告資訊;keep compiler quiet */
break;
}
//設定節點的ExecProcNode函式
ExecSetExecProcNode(result, result->ExecProcNode);
/*
* Initialize any initPlans present in this node. The planner put them in
* a separate list for us.
* 初始化該Plan節點中的所有initPlans.
* 計劃器把這些資訊放到一個單獨的連結串列中
*/
subps = NIL;//初始化
foreach(l, node->initPlan)//遍歷initPlan
{
SubPlan *subplan = (SubPlan *) lfirst(l);//子計劃
SubPlanState *sstate;//子計劃狀態
Assert(IsA(subplan, SubPlan));
sstate = ExecInitSubPlan(subplan, result);//初始化SubPlan
subps = lappend(subps, sstate);//新增到連結串列中
}
result->initPlan = subps;//賦值
/* Set up instrumentation for this node if requested */
//如需要,配置instrumentation
if (estate->es_instrument)
result->instrument = InstrAlloc(1, estate->es_instrument);
return result;
}
/*
* If a node wants to change its ExecProcNode function after ExecInitNode()
* has finished, it should do so with this function. That way any wrapper
* functions can be reinstalled, without the node having to know how that
* works.
* 如果一個節點想要在ExecInitNode()完成之後更改它的ExecProcNode函式,那麼它應該使用這個函式。
* 這樣就可以重新安裝任何包裝器函式,而不必讓節點知道它是如何工作的。
*/
void
ExecSetExecProcNode(PlanState *node, ExecProcNodeMtd function)
{
/*
* Add a wrapper around the ExecProcNode callback that checks stack depth
* during the first execution and maybe adds an instrumentation wrapper.
* When the callback is changed after execution has already begun that
* means we'll superfluously execute ExecProcNodeFirst, but that seems ok.
* 在ExecProcNode回撥函式新增一個包裝器,在第一次執行時檢查堆疊深度,可能還會新增一個檢測包裝器。
* 在執行已經開始之後,當回撥函式被更改時,這意味著再次執行ExecProcNodeFirst是多餘的,但這似乎是可以的。
*/
node->ExecProcNodeReal = function;
node->ExecProcNode = ExecProcNodeFirst;
}
/* ----------------------------------------------------------------
* ExecInitSeqScan
* 初始化順序掃描節點
* ----------------------------------------------------------------
*/
SeqScanState *
ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
{
SeqScanState *scanstate;
/*
* Once upon a time it was possible to have an outerPlan of a SeqScan, but
* not any more.
* 先前有可能存在外部的SeqScan計劃,但現在該做法已廢棄,這裡進行校驗
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
/*
* create state structure
* 建立SeqScanState資料結構體
*/
scanstate = makeNode(SeqScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
scanstate->ss.ps.ExecProcNode = ExecSeqScan;
/*
* Miscellaneous initialization
* 初始化
* create expression context for node
* 建立表示式上下文
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
/*
* open the scan relation
* 開啟掃描的Relation
*/
scanstate->ss.ss_currentRelation =
ExecOpenScanRelation(estate,
node->scanrelid,
eflags);
/* and create slot with the appropriate rowtype */
//使用合適的rowtype開啟slot
ExecInitScanTupleSlot(estate, &scanstate->ss,
RelationGetDescr(scanstate->ss.ss_currentRelation));
/*
* Initialize result type and projection.
* 初始化結果型別和投影
*/
ExecInitResultTypeTL(&scanstate->ss.ps);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
* initialize child expressions
* 初始化子表示式
*/
scanstate->ss.ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) scanstate);
return scanstate;
}
/* ----------------------------------------------------------------
* ExecOpenScanRelation
*
* Open the heap relation to be scanned by a base-level scan plan node.
* This should be called during the node's ExecInit routine.
* 開啟Heap Relation,由一個基表掃描計劃節點掃描。這應該在節點的ExecInit例程中呼叫。
* ----------------------------------------------------------------
*/
Relation
ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
{
Relation rel;
/* Open the relation. */
//開啟關係
rel = ExecGetRangeTableRelation(estate, scanrelid);
/*
* Complain if we're attempting a scan of an unscannable relation, except
* when the query won't actually be run. This is a slightly klugy place
* to do this, perhaps, but there is no better place.
* 給出提示資訊:試圖掃描一個不存在的關係。這可能是一個有點笨拙的地方,但沒有更好的提示了。
*/
if ((eflags & (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA)) == 0 &&
!RelationIsScannable(rel))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("materialized view \"%s\" has not been populated",
RelationGetRelationName(rel)),
errhint("Use the REFRESH MATERIALIZED VIEW command.")));
return rel;
}
/* ----------------------------------------------------------------
* ExecInitHashJoin
* 初始化Hash連線節點
* Init routine for HashJoin node.
* Hash連線透過遞迴呼叫ExecInitNode函式初始化參與連線的Relation
* ----------------------------------------------------------------
*/
HashJoinState *
ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
{
HashJoinState *hjstate;
Plan *outerNode;
Hash *hashNode;
List *lclauses;
List *rclauses;
List *rhclauses;
List *hoperators;
TupleDesc outerDesc,
innerDesc;
ListCell *l;
/* check for unsupported flags */
//校驗不支援的flags
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
/*
* create state structure
* 建立state資料結構體
*/
hjstate = makeNode(HashJoinState);
hjstate->js.ps.plan = (Plan *) node;
hjstate->js.ps.state = estate;
/*
* See ExecHashJoinInitializeDSM() and ExecHashJoinInitializeWorker()
* where this function may be replaced with a parallel version, if we
* managed to launch a parallel query.
* 請參閱ExecHashJoinInitializeWorker()和ExecHashJoinInitializeWorker(),
* 如果成功啟動了並行查詢,這個函式可以用一個並行版本替換。
*/
hjstate->js.ps.ExecProcNode = ExecHashJoin;
hjstate->js.jointype = node->join.jointype;
/*
* Miscellaneous initialization
* 初始化
* create expression context for node
*/
ExecAssignExprContext(estate, &hjstate->js.ps);
/*
* initialize child nodes
* 初始化子節點
*
* Note: we could suppress the REWIND flag for the inner input, which
* would amount to betting that the hash will be a single batch. Not
* clear if this would be a win or not.
* 注意:可以禁止內部輸入的REWIND標誌,這相當於打賭雜湊將是單個批處理。
* 不清楚這是否會是一場勝利。
*/
outerNode = outerPlan(node);
hashNode = (Hash *) innerPlan(node);
outerPlanState(hjstate) = ExecInitNode(outerNode, estate, eflags);//遞迴處理外表節點
outerDesc = ExecGetResultType(outerPlanState(hjstate));//
innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);//遞迴處理內表節點
innerDesc = ExecGetResultType(innerPlanState(hjstate));
/*
* Initialize result slot, type and projection.
* 初始化節點slot/型別和投影
*/
ExecInitResultTupleSlotTL(&hjstate->js.ps);
ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
/*
* tuple table initialization
* 元組表初始化
*/
hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc);
/*
* detect whether we need only consider the first matching inner tuple
* 檢測是否只需要考慮內表元組的首次匹配
*/
hjstate->js.single_match = (node->join.inner_unique ||
node->join.jointype == JOIN_SEMI);
/* set up null tuples for outer joins, if needed */
//配置外連線的NULL元組
switch (node->join.jointype)
{
case JOIN_INNER:
case JOIN_SEMI:
break;
case JOIN_LEFT:
case JOIN_ANTI://左連線&半連線
hjstate->hj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate, innerDesc);
break;
case JOIN_RIGHT:
hjstate->hj_NullOuterTupleSlot =
ExecInitNullTupleSlot(estate, outerDesc);
break;
case JOIN_FULL:
hjstate->hj_NullOuterTupleSlot =
ExecInitNullTupleSlot(estate, outerDesc);
hjstate->hj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate, innerDesc);
break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) node->join.jointype);
}
/*
* now for some voodoo. our temporary tuple slot is actually the result
* tuple slot of the Hash node (which is our inner plan). we can do this
* because Hash nodes don't return tuples via ExecProcNode() -- instead
* the hash join node uses ExecScanHashBucket() to get at the contents of
* the hash table. -cim 6/9/91
* 現在來點巫術。
* 臨時tuple槽實際上是雜湊節點的結果tuple槽(這是我們的內部計劃)。
* 之可以這樣做,是因為雜湊節點不會透過ExecProcNode()返回元組——
* 相反,雜湊連線節點使用ExecScanHashBucket()來獲取雜湊表的內容。
* by cim 6/9/91
*/
{
HashState *hashstate = (HashState *) innerPlanState(hjstate);
TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;
hjstate->hj_HashTupleSlot = slot;
}
/*
* initialize child expressions
* 初始化子表示式
*/
hjstate->js.ps.qual =
ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
hjstate->js.joinqual =
ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
hjstate->hashclauses =
ExecInitQual(node->hashclauses, (PlanState *) hjstate);
/*
* initialize hash-specific info
* 初始化hash相關的資訊
*/
hjstate->hj_HashTable = NULL;
hjstate->hj_FirstOuterTupleSlot = NULL;
hjstate->hj_CurHashValue = 0;
hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
hjstate->hj_CurTuple = NULL;
/*
* Deconstruct the hash clauses into outer and inner argument values, so
* that we can evaluate those subexpressions separately. Also make a list
* of the hash operator OIDs, in preparation for looking up the hash
* functions to use.
* 將雜湊子句解構為外部和內部引數值,以便能夠分別計算這些子表示式。
* 還可以列出雜湊運算子oid,以便查詢要使用的雜湊函式。
*/
lclauses = NIL;
rclauses = NIL;
rhclauses = NIL;
hoperators = NIL;
foreach(l, node->hashclauses)
{
OpExpr *hclause = lfirst_node(OpExpr, l);
lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args),
(PlanState *) hjstate));
rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args),
(PlanState *) hjstate));
rhclauses = lappend(rhclauses, ExecInitExpr(lsecond(hclause->args),
innerPlanState(hjstate)));
hoperators = lappend_oid(hoperators, hclause->opno);
}
hjstate->hj_OuterHashKeys = lclauses;
hjstate->hj_InnerHashKeys = rclauses;
hjstate->hj_HashOperators = hoperators;
/* child Hash node needs to evaluate inner hash keys, too */
((HashState *) innerPlanState(hjstate))->hashkeys = rhclauses;
hjstate->hj_JoinState = HJ_BUILD_HASHTABLE;
hjstate->hj_MatchedOuter = false;
hjstate->hj_OuterNotEmpty = false;
return hjstate;
}
三、跟蹤分析
測試指令碼如下
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-# order by dw.dwbh;
QUERY PLAN
------------------------------------------------------------------------------------------
Sort (cost=20070.93..20320.93 rows=100000 width=47)
Sort Key: dw.dwbh
-> Hash Join (cost=3754.00..8689.61 rows=100000 width=47)
Hash Cond: ((gr.dwbh)::text = (dw.dwbh)::text)
-> Hash Join (cost=3465.00..8138.00 rows=100000 width=31)
Hash Cond: ((jf.grbh)::text = (gr.grbh)::text)
-> Seq Scan on t_jfxx jf (cost=0.00..1637.00 rows=100000 width=20)
-> Hash (cost=1726.00..1726.00 rows=100000 width=16)
-> Seq Scan on t_grxx gr (cost=0.00..1726.00 rows=100000 width=16)
-> Hash (cost=164.00..164.00 rows=10000 width=20)
-> Seq Scan on t_dwxx dw (cost=0.00..164.00 rows=10000 width=20)
(11 rows)
啟動gdb,設定斷點,進入ExecInitNode
(gdb) b ExecInitNode
Breakpoint 1 at 0x6e3b90: file execProcnode.c, line 148.
(gdb) c
Continuing.
Breakpoint 1, ExecInitNode (node=0x1b71f90, estate=0x1b78f48, eflags=16) at execProcnode.c:148
warning: Source file is more recent than executable.
148 if (node == NULL)
輸入引數,node為T_Sort
(gdb) p *node
$1 = {type = T_Sort, startup_cost = 20070.931487218411, total_cost = 20320.931487218411, plan_rows = 100000,
plan_width = 47, parallel_aware = false, parallel_safe = true, plan_node_id = 0, targetlist = 0x1b762c0, qual = 0x0,
lefttree = 0x1b75728, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
(gdb) p *estate
$2 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x1b31e10, es_crosscheck_snapshot = 0x0,
es_range_table = 0x1b75c00, es_plannedstmt = 0x1b77d58,
es_sourceText = 0x1a8ceb8 "select dw.*,grjf.grbh,grjf.xm,grjf.ny,grjf.je \nfrom t_dwxx dw,lateral (select gr.grbh,gr.xm,jf.ny,jf.je \n", ' ' <repeats 24 times>, "from t_grxx gr inner join t_jfxx jf \n", ' ' <repeats 34 times>...,
es_junkFilter = 0x0, es_output_cid = 0, es_result_relations = 0x0, es_num_result_relations = 0,
es_result_relation_info = 0x0, es_root_result_relations = 0x0, es_num_root_result_relations = 0,
es_tuple_routing_result_relations = 0x0, es_trig_target_relations = 0x0, es_trig_tuple_slot = 0x0,
es_trig_oldtup_slot = 0x0, es_trig_newtup_slot = 0x0, es_param_list_info = 0x0, es_param_exec_vals = 0x0,
es_queryEnv = 0x0, es_query_cxt = 0x1b78e30, es_tupleTable = 0x0, es_rowMarks = 0x0, es_processed = 0, es_lastoid = 0,
es_top_eflags = 16, es_instrument = 0, es_finished = false, es_exprcontexts = 0x0, es_subplanstates = 0x0,
es_auxmodifytables = 0x0, es_per_tuple_exprcontext = 0x0, es_epqTuple = 0x0, es_epqTupleSet = 0x0, es_epqScanDone = 0x0,
es_use_parallel_mode = false, es_query_dsa = 0x0, es_jit_flags = 0, es_jit = 0x0, es_jit_worker_instr = 0x0}
檢查堆疊
(gdb) n
156 check_stack_depth();
進入相應的處理邏輯
158 switch (nodeTag(node))
(gdb)
313 result = (PlanState *) ExecInitSort((Sort *) node,
返回結果
(gdb) p *result
$3 = {type = 11084746, plan = 0x69900000699, state = 0x0, ExecProcNode = 0x0, ExecProcNodeReal = 0x0,
instrument = 0x1000000000000, worker_instrument = 0x0, worker_jit_instrument = 0x200000001, qual = 0x0, lefttree = 0x0,
righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x7f7f7f7f7f7f7f7e, ps_ResultTupleSlot = 0x7f7f7f7f7f7f7f7f,
ps_ExprContext = 0x7f7f7f7f7f7f7f7f, ps_ProjInfo = 0x80, scandesc = 0x0}
設定斷點,進入ExecSetExecProcNode
(gdb) b ExecSetExecProcNode
Breakpoint 2 at 0x6e41a1: file execProcnode.c, line 414.
(gdb) c
Continuing.
ExecSetExecProcNode->輸入引數,function為ExecSeqScan,在實際執行時呼叫此函式
(gdb) p *function
$5 = {TupleTableSlot *(struct PlanState *)} 0x714d59 <ExecSeqScan>
(gdb) p *node
$6 = {type = T_SeqScanState, plan = 0x1b74f58, state = 0x1b78f48, ExecProcNode = 0x714d59 <ExecSeqScan>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x1b7a9c0,
ps_ExprContext = 0x1b7a5a8, ps_ProjInfo = 0x1b7aa80, scandesc = 0x7f07174f5308}
回到最上層的ExecInitNode,initPlan為NULL
(gdb)
ExecInitNode (node=0x1b74f58, estate=0x1b78f48, eflags=16) at execProcnode.c:379
379 subps = NIL;
(gdb)
380 foreach(l, node->initPlan)
(gdb) p *node
$7 = {type = T_SeqScan, startup_cost = 0, total_cost = 1726, plan_rows = 100000, plan_width = 16, parallel_aware = false,
parallel_safe = true, plan_node_id = 5, targetlist = 0x1b74e20, qual = 0x0, lefttree = 0x0, righttree = 0x0,
initPlan = 0x0, extParam = 0x0, allParam = 0x0}
完成呼叫
(gdb) n
389 result->initPlan = subps;
(gdb)
392 if (estate->es_instrument)
(gdb)
395 return result;
(gdb)
396 }
下面重點考察ExecInitSeqScan和ExecInitHashJoin,首先是ExecInitHashJoin
ExecInitHashJoin->
(gdb) b ExecInitSeqScan
Breakpoint 3 at 0x714daf: file nodeSeqscan.c, line 148.
(gdb) b ExecInitHashJoin
Breakpoint 4 at 0x701f60: file nodeHashjoin.c, line 604.
(gdb) c
Continuing.
Breakpoint 4, ExecInitHashJoin (node=0x1b737c0, estate=0x1b78f48, eflags=16) at nodeHashjoin.c:604
warning: Source file is more recent than executable.
604 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
ExecInitHashJoin->校驗並初始化
604 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
(gdb) n
609 hjstate = makeNode(HashJoinState);
(gdb)
610 hjstate->js.ps.plan = (Plan *) node;
(gdb)
611 hjstate->js.ps.state = estate;
(gdb)
618 hjstate->js.ps.ExecProcNode = ExecHashJoin;
(gdb)
619 hjstate->js.jointype = node->join.jointype;
(gdb)
626 ExecAssignExprContext(estate, &hjstate->js.ps);
ExecInitHashJoin->初步的資料結構體
(gdb) p *hjstate
$8 = {js = {ps = {type = T_HashJoinState, plan = 0x1b737c0, state = 0x1b78f48, ExecProcNode = 0x701efa <ExecHashJoin>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x0,
ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x0}, jointype = JOIN_INNER, single_match = false,
joinqual = 0x0}, hashclauses = 0x0, hj_OuterHashKeys = 0x0, hj_InnerHashKeys = 0x0, hj_HashOperators = 0x0,
hj_HashTable = 0x0, hj_CurHashValue = 0, hj_CurBucketNo = 0, hj_CurSkewBucketNo = 0, hj_CurTuple = 0x0,
hj_OuterTupleSlot = 0x0, hj_HashTupleSlot = 0x0, hj_NullOuterTupleSlot = 0x0, hj_NullInnerTupleSlot = 0x0,
hj_FirstOuterTupleSlot = 0x0, hj_JoinState = 0, hj_MatchedOuter = false, hj_OuterNotEmpty = false}
ExecInitHashJoin->獲取HashJoin的outer&inner(PG視為Hash節點)
outerNode為HashJoin,innerNode為Hash
(gdb) n
635 outerNode = outerPlan(node);
gdb) n
636 hashNode = (Hash *) innerPlan(node);
(gdb)
638 outerPlanState(hjstate) = ExecInitNode(outerNode, estate, eflags);
(gdb) p *node
$9 = {join = {plan = {type = T_HashJoin, startup_cost = 3754, total_cost = 8689.6112499999999, plan_rows = 100000,
plan_width = 47, parallel_aware = false, parallel_safe = true, plan_node_id = 1, targetlist = 0x1b74cc8, qual = 0x0,
lefttree = 0x1b73320, righttree = 0x1b73728, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, jointype = JOIN_INNER,
inner_unique = true, joinqual = 0x0}, hashclauses = 0x1b74bb8}
(gdb) p *outerNode
$12 = {type = T_HashJoin, startup_cost = 3465, total_cost = 8138, plan_rows = 100000, plan_width = 31,
parallel_aware = false, parallel_safe = true, plan_node_id = 2, targetlist = 0x1b75588, qual = 0x0, lefttree = 0x1b72da0,
righttree = 0x1b73288, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
(gdb) p *hashNode
$11 = {plan = {type = T_Hash, startup_cost = 164, total_cost = 164, plan_rows = 10000, plan_width = 20,
parallel_aware = false, parallel_safe = true, plan_node_id = 6, targetlist = 0x1b75c08, qual = 0x0,
lefttree = 0x1b73570, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}, skewTable = 16742,
skewColumn = 1, skewInherit = false, rows_total = 0}
ExecInitHashJoin->進入outerNode的HashJoin,直接跳過
(gdb) n
Breakpoint 4, ExecInitHashJoin (node=0x1b73320, estate=0x1b78f48, eflags=16) at nodeHashjoin.c:604
604 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
(gdb) finish
Run till exit from #0 ExecInitHashJoin (node=0x1b73320, estate=0x1b78f48, eflags=16) at nodeHashjoin.c:604
ExecInitHashJoin->進入innerNode的呼叫(ExecInitSeqScan)
Breakpoint 3, ExecInitSeqScan (node=0x1b72da0, estate=0x1b78f48, eflags=16) at nodeSeqscan.c:148
warning: Source file is more recent than executable.
148 Assert(outerPlan(node) == NULL);
(gdb)
ExecInitSeqScan
ExecInitSeqScan->執行校驗,並建立Node.
注意:ExecProcNode=ExecSeqScan
148 Assert(outerPlan(node) == NULL);
(gdb) n
149 Assert(innerPlan(node) == NULL);
(gdb)
154 scanstate = makeNode(SeqScanState);
(gdb)
155 scanstate->ss.ps.plan = (Plan *) node;
(gdb)
156 scanstate->ss.ps.state = estate;
(gdb)
157 scanstate->ss.ps.ExecProcNode = ExecSeqScan;
(gdb)
164 ExecAssignExprContext(estate, &scanstate->ss.ps);
(gdb) p *scanstate
$1 = {ss = {ps = {type = T_SeqScanState, plan = 0x1b72da0, state = 0x1b78f48, ExecProcNode = 0x714d59 <ExecSeqScan>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x0,
ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x0}, ss_currentRelation = 0x0, ss_currentScanDesc = 0x0,
ss_ScanTupleSlot = 0x0}, pscan_len = 0}
ExecInitSeqScan->開啟Relation
gdb) n
173 ExecOpenScanRelation(estate,
(gdb)
172 scanstate->ss.ss_currentRelation =
(gdb)
179 RelationGetDescr(scanstate->ss.ss_currentRelation));
(gdb) p *scanstate->ss.ss_currentRelation
$3 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 16747}, rd_smgr = 0x1b64650, rd_refcnt = 1, rd_backend = -1,
rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 1 '\001', rd_statvalid = true,
rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7f07174f5c78, rd_att = 0x7f07174f5d90, rd_id = 16747,
rd_lockInfo = {lockRelId = {relId = 16747, dbId = 16402}}, rd_rules = 0x0, rd_rulescxt = 0x0, trigdesc = 0x0,
rd_rsdesc = 0x0, rd_fkeylist = 0x0, rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0,
rd_partdesc = 0x0, rd_partcheck = 0x0, rd_indexlist = 0x7f0717447328, rd_oidindex = 0, rd_pkindex = 0,
rd_replidindex = 0, rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = 0x0,
rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0,
rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0, rd_opcintype = 0x0, rd_support = 0x0,
rd_supportinfo = 0x0, rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0,
rd_exclstrats = 0x0, rd_amcache = 0x0, rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0,
pgstat_info = 0x1b0b6c0}
ExecInitSeqScan->使用合適的rowtype開啟slot(初始化ScanTupleSlot)
(gdb)
178 ExecInitScanTupleSlot(estate, &scanstate->ss,
(gdb)
184 ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
(gdb) p *scanstate->ss.ss_ScanTupleSlot
$4 = {type = T_TupleTableSlot, tts_isempty = true, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false,
tts_tuple = 0x0, tts_tupleDescriptor = 0x7f07174f5d90, tts_mcxt = 0x1b78e30, tts_buffer = 0, tts_nvalid = 0,
tts_values = 0x1b79a98, tts_isnull = 0x1b79ab0, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}
(gdb)
ExecInitSeqScan->初始化結果型別和投影
(gdb) n
185 ExecAssignScanProjectionInfo(&scanstate->ss);
(gdb)
191 ExecInitQual(node->plan.qual, (PlanState *) scanstate);
(gdb) p *scanstate
$5 = {ss = {ps = {type = T_SeqScanState, plan = 0x1b72da0, state = 0x1b78f48, ExecProcNode = 0x714d59 <ExecSeqScan>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x1b79d48,
ps_ExprContext = 0x1b79978, ps_ProjInfo = 0x1b79e08, scandesc = 0x7f07174f5d90}, ss_currentRelation = 0x7f07174f5a60,
ss_currentScanDesc = 0x0, ss_ScanTupleSlot = 0x1b79a38}, pscan_len = 0}
(gdb) p *scanstate->ss.ps.ps_ResultTupleSlot
$6 = {type = T_TupleTableSlot, tts_isempty = true, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false,
tts_tuple = 0x0, tts_tupleDescriptor = 0x1b79b30, tts_mcxt = 0x1b78e30, tts_buffer = 0, tts_nvalid = 0,
tts_values = 0x1b79da8, tts_isnull = 0x1b79dc0, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}
(gdb) p *scanstate->ss.ps.ps_ProjInfo
$7 = {type = T_ProjectionInfo, pi_state = {tag = {type = T_ExprState}, flags = 6 '\006', resnull = false, resvalue = 0,
resultslot = 0x1b79d48, steps = 0x1b79ea0, evalfunc = 0x6d104b <ExecInterpExprStillValid>, expr = 0x1b72c68,
evalfunc_private = 0x6cec02 <ExecInterpExpr>, steps_len = 5, steps_alloc = 16, parent = 0x1b79860, ext_params = 0x0,
innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0, innermost_domainnull = 0x0},
pi_exprContext = 0x1b79978}
ExecInitSeqScan->初始化子條件表示式(為NULL),返回結果
(gdb) n
190 scanstate->ss.ps.qual =
(gdb)
193 return scanstate;
(gdb) p *scanstate->ss.ps.qual
Cannot access memory at address 0x0
ExecInitSeqScan->回到ExecInitNode for Node SeqScan
(gdb) n
194 }
(gdb)
ExecInitNode (node=0x1b72da0, estate=0x1b78f48, eflags=16) at execProcnode.c:209
warning: Source file is more recent than executable.
209 break;
ExecInitSeqScan->回到ExecInitNode,結束呼叫
(gdb) n
379 subps = NIL;
(gdb)
380 foreach(l, node->initPlan)
(gdb)
389 result->initPlan = subps;
(gdb)
392 if (estate->es_instrument)
(gdb)
395 return result;
(gdb)
396 }
(gdb)
ExecInitSeqScan->回到ExecInitHashJoin
(gdb)
ExecInitHashJoin (node=0x1b73320, estate=0x1b78f48, eflags=16) at nodeHashjoin.c:639
639 outerDesc = ExecGetResultType(outerPlanState(hjstate));
ExecInitHashJoin
ExecInitHashJoin->完成outer relation的處理,開始處理inner relation(遞迴呼叫ExecInitNode)
639 outerDesc = ExecGetResultType(outerPlanState(hjstate));
(gdb) n
640 innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
(gdb)
Breakpoint 2, ExecInitSeqScan (node=0x1b72ff0, estate=0x1b78f48, eflags=16) at nodeSeqscan.c:148
148 Assert(outerPlan(node) == NULL);
(gdb) del 2
(gdb) finish
Run till exit from #0 ExecInitSeqScan (node=0x1b72ff0, estate=0x1b78f48, eflags=16) at nodeSeqscan.c:148
0x00000000006e3cd2 in ExecInitNode (node=0x1b72ff0, estate=0x1b78f48, eflags=16) at execProcnode.c:207
207 result = (PlanState *) ExecInitSeqScan((SeqScan *) node,
Value returned is $10 = (SeqScanState *) 0x1b7a490
(gdb)
...
(gdb) n
641 innerDesc = ExecGetResultType(innerPlanState(hjstate));
ExecInitHashJoin->檢視outerDesc和innerDesc
(gdb) p *outerDesc
$14 = {natts = 3, tdtypeid = 2249, tdtypmod = -1, tdhasoid = false, tdrefcount = -1, constr = 0x0, attrs = 0x1b79b50}
(gdb) p *innerDesc
$15 = {natts = 3, tdtypeid = 2249, tdtypmod = -1, tdhasoid = false, tdrefcount = -1, constr = 0x0, attrs = 0x1b7ab38}
(gdb)
ExecInitHashJoin->初始化節點slot/型別/投影/元組表等
(gdb) n
647 ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
(gdb)
652 hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc);
(gdb)
657 hjstate->js.single_match = (node->join.inner_unique ||
(gdb)
658 node->join.jointype == JOIN_SEMI);
(gdb) p *hjstate
$16 = {js = {ps = {type = T_HashJoinState, plan = 0x1b73320, state = 0x1b78f48, ExecProcNode = 0x701efa <ExecHashJoin>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x1b79860, righttree = 0x1b7a2b8, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0,
ps_ResultTupleSlot = 0x1b7ad30, ps_ExprContext = 0x1b797a0, ps_ProjInfo = 0x1b85bf8, scandesc = 0x0},
jointype = JOIN_INNER, single_match = false, joinqual = 0x0}, hashclauses = 0x0, hj_OuterHashKeys = 0x0,
hj_InnerHashKeys = 0x0, hj_HashOperators = 0x0, hj_HashTable = 0x0, hj_CurHashValue = 0, hj_CurBucketNo = 0,
hj_CurSkewBucketNo = 0, hj_CurTuple = 0x0, hj_OuterTupleSlot = 0x1b860a8, hj_HashTupleSlot = 0x0,
hj_NullOuterTupleSlot = 0x0, hj_NullInnerTupleSlot = 0x0, hj_FirstOuterTupleSlot = 0x0, hj_JoinState = 0,
hj_MatchedOuter = false, hj_OuterNotEmpty = false}
(gdb) p *hjstate->js.ps.ps_ResultTupleSlot #結果TupleSlot
$20 = {type = T_TupleTableSlot, tts_isempty = true, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false,
tts_tuple = 0x0, tts_tupleDescriptor = 0x1b857b8, tts_mcxt = 0x1b78e30, tts_buffer = 0, tts_nvalid = 0,
tts_values = 0x1b7ad90, tts_isnull = 0x1b7adb8, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}
(gdb) p *hjstate->js.ps.ps_ProjInfo #投影
$18 = {type = T_ProjectionInfo, pi_state = {tag = {type = T_ExprState}, flags = 6 '\006', resnull = false, resvalue = 0,
resultslot = 0x1b7ad30, steps = 0x1b85c90, evalfunc = 0x6d104b <ExecInterpExprStillValid>, expr = 0x1b75588,
evalfunc_private = 0x6cec02 <ExecInterpExpr>, steps_len = 8, steps_alloc = 16, parent = 0x1b79588, ext_params = 0x0,
innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0, innermost_domainnull = 0x0},
pi_exprContext = 0x1b797a0}
(gdb) p *hjstate->hj_OuterTupleSlot #元組slot
$19 = {type = T_TupleTableSlot, tts_isempty = true, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false,
tts_tuple = 0x0, tts_tupleDescriptor = 0x1b79b30, tts_mcxt = 0x1b78e30, tts_buffer = 0, tts_nvalid = 0,
tts_values = 0x1b86108, tts_isnull = 0x1b86120, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}
(gdb) n
657 hjstate->js.single_match = (node->join.inner_unique ||
(gdb)
661 switch (node->join.jointype)
(gdb) p hjstate->js.single_match
$21 = false
ExecInitHashJoin->配置外連線的NULL元組(不需要)
(gdb) n
665 break;
ExecInitHashJoin->獲取Hash操作的State,注意ExecProcNode是一個包裝器(ExecProcNodeFirst),實際的函式是ExecHash
(gdb)
694 HashState *hashstate = (HashState *) innerPlanState(hjstate);
(gdb)
695 TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;
(gdb) n
697 hjstate->hj_HashTupleSlot = slot;
(gdb)
(gdb) p *hashstate
$22 = {ps = {type = T_HashState, plan = 0x1b73288, state = 0x1b78f48, ExecProcNode = 0x6e41bb <ExecProcNodeFirst>,
ExecProcNodeReal = 0x6fb5f2 <ExecHash>, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0,
qual = 0x0, lefttree = 0x1b7a490, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0,
ps_ResultTupleSlot = 0x1b856f8, ps_ExprContext = 0x1b7a3d0, ps_ProjInfo = 0x0, scandesc = 0x0}, hashtable = 0x0,
hashkeys = 0x0, shared_info = 0x0, hinstrument = 0x0, parallel_state = 0x0}
ExecInitHashJoin->初始化(子)表示式,均為NULL
(gdb) n
697 hjstate->hj_HashTupleSlot = slot;
(gdb)
704 ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
(gdb) n
703 hjstate->js.ps.qual =
(gdb)
706 ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
(gdb)
705 hjstate->js.joinqual =
(gdb)
708 ExecInitQual(node->hashclauses, (PlanState *) hjstate);
(gdb)
707 hjstate->hashclauses =
(gdb)
713 hjstate->hj_HashTable = NULL;
(gdb)
714 hjstate->hj_FirstOuterTupleSlot = NULL;
(gdb)
716 hjstate->hj_CurHashValue = 0;
(gdb)
717 hjstate->hj_CurBucketNo = 0;
(gdb)
718 hjstate->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
(gdb)
719 hjstate->hj_CurTuple = NULL;
(gdb)
727 lclauses = NIL;
(gdb) p *hjstate->js.ps.qual
Cannot access memory at address 0x0
(gdb) p *hjstate->js.joinqual
Cannot access memory at address 0x0
(gdb)
ExecInitHashJoin->將雜湊子句解構為外部和內部引數值,以便能夠分別計算這些子表示式;還可以列出雜湊運算子oid,以便查詢要使用的雜湊函式.
...
(gdb) p *hjstate->hj_OuterHashKeys
$25 = {type = T_List, length = 1, head = 0x1b86f98, tail = 0x1b86f98}
(gdb) p *hjstate->hj_InnerHashKeys
$26 = {type = T_List, length = 1, head = 0x1b87708, tail = 0x1b87708}
(gdb) p *hjstate->hj_HashOperators
$27 = {type = T_OidList, length = 1, head = 0x1b87768, tail = 0x1b87768}
(gdb)
ExecInitHashJoin->完成呼叫
(gdb) n
746 hjstate->hj_JoinState = HJ_BUILD_HASHTABLE;
(gdb)
747 hjstate->hj_MatchedOuter = false;
(gdb)
748 hjstate->hj_OuterNotEmpty = false;
(gdb)
750 return hjstate;
(gdb)
751 }
ExecInitHashJoin->最終結果(注意:這是最上層的HashJoin)
(gdb) p *hjstate
$28 = {js = {ps = {type = T_HashJoinState, plan = 0x1b73320, state = 0x1b78f48, ExecProcNode = 0x701efa <ExecHashJoin>,
ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0,
lefttree = 0x1b79860, righttree = 0x1b7a2b8, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0,
ps_ResultTupleSlot = 0x1b7ad30, ps_ExprContext = 0x1b797a0, ps_ProjInfo = 0x1b85bf8, scandesc = 0x0},
jointype = JOIN_INNER, single_match = false, joinqual = 0x0}, hashclauses = 0x1b86168, hj_OuterHashKeys = 0x1b86fc0,
hj_InnerHashKeys = 0x1b87730, hj_HashOperators = 0x1b87790, hj_HashTable = 0x0, hj_CurHashValue = 0, hj_CurBucketNo = 0,
hj_CurSkewBucketNo = -1, hj_CurTuple = 0x0, hj_OuterTupleSlot = 0x1b860a8, hj_HashTupleSlot = 0x1b856f8,
hj_NullOuterTupleSlot = 0x0, hj_NullInnerTupleSlot = 0x0, hj_FirstOuterTupleSlot = 0x0, hj_JoinState = 1,
hj_MatchedOuter = false, hj_OuterNotEmpty = false}
DONE!
四、參考資料
PG Document:Query Planning
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374805/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PostgreSQL 原始碼解讀(86)- 查詢語句#71(PortalRun->PortalR...SQL原始碼
- PostgreSQL 原始碼解讀(24)- 查詢語句#9(查詢重寫)SQL原始碼
- PostgreSQL 原始碼解讀(20)- 查詢語句#5(查詢樹Query詳解)SQL原始碼
- PostgreSQL 原始碼解讀(70)- 查詢語句#55(make_one_rel函式#20-...SQL原始碼函式
- PostgreSQL 原始碼解讀(18)- 查詢語句#3(SQL Parse)SQL原始碼
- PostgreSQL 原始碼解讀(19)- 查詢語句#4(ParseTree詳解)SQL原始碼
- PostgreSQL 原始碼解讀(17)- 查詢語句#2(查詢優化基礎)SQL原始碼優化
- PostgreSQL 原始碼解讀(25)- 查詢語句#10(查詢優化概覽)SQL原始碼優化
- PostgreSQL 原始碼解讀(83)- 查詢語句#68(PortalStart函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(75)- 查詢語句#60(Review - standard_...SQL原始碼View
- PostgreSQL 原始碼解讀(74)- 查詢語句#59(Review - subquery_...SQL原始碼View
- PostgreSQL 原始碼解讀(42)- 查詢語句#27(等價類)SQL原始碼
- PostgreSQL 原始碼解讀(29)- 查詢語句#14(查詢優化-上拉子查詢)SQL原始碼優化
- PostgreSQL 原始碼解讀(37)- 查詢語句#22(查詢優化-grouping_plan...SQL原始碼優化
- PostgreSQL 原始碼解讀(82)- 查詢語句#67(PortalXXX系列函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(81)- 查詢語句#66(Review - exec_simp...SQL原始碼View
- 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 原始碼解讀(87)- 查詢語句#72(PortalRunSelect->E...SQL原始碼
- PostgreSQL 原始碼解讀(84)- 查詢語句#69(PortalStart->InitP...SQL原始碼
- PostgreSQL 原始碼解讀(93)- 查詢語句#77(ExecHashJoin函式#3)SQL原始碼函式
- PostgreSQL 原始碼解讀(50)- 查詢語句#35(Optimizer Review#1)SQL原始碼View
- PostgreSQL 原始碼解讀(51)- 查詢語句#36(Optimizer Review#2)SQL原始碼View
- PostgreSQL 原始碼解讀(36)- 查詢語句#21(查詢優化-消除外連線)SQL原始碼優化
- PostgreSQL 原始碼解讀(21)- 查詢語句#6(PlannedStmt詳解-跟蹤分析)SQL原始碼
- PostgreSQL 原始碼解讀(73)- 查詢語句#58(grouping_planner函式...SQL原始碼函式
- PostgreSQL 原始碼解讀(23)- 查詢語句#8(PlannedStmt與QUERY P...SQL原始碼
- PostgreSQL 原始碼解讀(95)- 查詢語句#78(ExecHashJoin函式#4-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(97)- 查詢語句#79(ExecHashJoin函式#5-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(16)- 查詢語句#1(基礎:關係代數)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原始碼函式