您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中ExecutePlan函數(shù)與ExecSeqScan函數(shù)的作用是什么”,在日常操作中,相信很多人在PostgreSQL中ExecutePlan函數(shù)與ExecSeqScan函數(shù)的作用是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”PostgreSQL中ExecutePlan函數(shù)與ExecSeqScan函數(shù)的作用是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
ExecutePlan函數(shù)處理查詢計(jì)劃,直到檢索到指定數(shù)量(參數(shù)numbertuple)的元組,并沿著指定的方向掃描。ExecSeqScan函數(shù)順序掃描relation,返回下一個(gè)符合條件的元組。
Plan
所有計(jì)劃節(jié)點(diǎn)通過(guò)將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)通過(guò)將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;
ExecutePlan
PortalRunSelect->ExecutorRun->ExecutePlan函數(shù)處理查詢計(jì)劃,直到檢索到指定數(shù)量(參數(shù)numbertuple)的元組,并沿著指定的方向掃描.
/* ---------------------------------------------------------------- * ExecutePlan * * Processes the query plan until we have retrieved 'numberTuples' tuples, * moving in the specified direction. * 處理查詢計(jì)劃,直到檢索到指定數(shù)量(參數(shù)numbertuple)的元組,并沿著指定的方向移動(dòng)。 * * Runs to completion if numberTuples is 0 * 如參數(shù)numbertuple為0,則運(yùn)行至結(jié)束為止 * * Note: the ctid attribute is a 'junk' attribute that is removed before the * user can see it * 注意:ctid屬性是"junk"屬性,在返回給用戶前會(huì)移除 * ---------------------------------------------------------------- */ static void ExecutePlan(EState *estate,//執(zhí)行狀態(tài) PlanState *planstate,//計(jì)劃狀態(tài) bool use_parallel_mode,//是否使用并行模式 CmdType operation,//操作類型 bool sendTuples,//是否需要傳輸元組 uint64 numberTuples,//元組數(shù)量 ScanDirection direction,//掃描方向 DestReceiver *dest,//接收的目標(biāo)端 bool execute_once)//是否只執(zhí)行一次 { TupleTableSlot *slot;//元組表Slot uint64 current_tuple_count;//當(dāng)前的元組計(jì)數(shù) /* * initialize local variables * 初始化本地變量 */ current_tuple_count = 0; /* * Set the direction. * 設(shè)置掃描方向 */ estate->es_direction = direction; /* * If the plan might potentially be executed multiple times, we must force * it to run without parallelism, because we might exit early. * 如果計(jì)劃可能被多次執(zhí)行,那么必須強(qiáng)制它在非并行的情況下運(yùn)行,因?yàn)榭赡軙?huì)提前退出。 */ if (!execute_once) use_parallel_mode = false;//如需多次執(zhí)行,則不允許并行執(zhí)行 estate->es_use_parallel_mode = use_parallel_mode; if (use_parallel_mode) EnterParallelMode();//如并行,則進(jìn)入并行模式 /* * Loop until we've processed the proper number of tuples from the plan. * 循環(huán)直至執(zhí)行計(jì)劃已處理完成相應(yīng)數(shù)量的元組 * 注意:每次循環(huán)只處理一個(gè)元組,每次都要重置元組Expr的上下文/過(guò)濾不需要的列/發(fā)送元組 */ for (;;) { /* Reset the per-output-tuple exprcontext */ //重置Expr上下文 ResetPerTupleExprContext(estate); /* * Execute the plan and obtain a tuple * 執(zhí)行計(jì)劃,獲取一個(gè)元組 */ slot = ExecProcNode(planstate); /* * if the tuple is null, then we assume there is nothing more to * process so we just end the loop... * 如果返回的元組為空,那么可以認(rèn)為沒(méi)有什么要處理的了,結(jié)束循環(huán)…… */ if (TupIsNull(slot)) { /* * If we know we won't need to back up, we can release resources * at this point. * 如果已知不需要備份(回溯),那么可以釋放資源了 */ if (!(estate->es_top_eflags & EXEC_FLAG_BACKWARD)) (void) ExecShutdownNode(planstate); break; } /* * If we have a junk filter, then project a new tuple with the junk * removed. * 如有junk過(guò)濾器,使用junk執(zhí)行投影操作,產(chǎn)生一個(gè)新的元組 * * Store this new "clean" tuple in the junkfilter's resultSlot. * (Formerly, we stored it back over the "dirty" tuple, which is WRONG * because that tuple slot has the wrong descriptor.) * 將這個(gè)新的“clean”元組存儲(chǔ)在junkfilter的resultSlot中。 * (以前,將其存儲(chǔ)在“dirty” tuple上,這是錯(cuò)誤的,因?yàn)樵搕uple slot的描述符是錯(cuò)誤的。) */ if (estate->es_junkFilter != NULL) slot = ExecFilterJunk(estate->es_junkFilter, slot); /* * If we are supposed to send the tuple somewhere, do so. (In * practice, this is probably always the case at this point.) * 如果要將元組發(fā)送到某個(gè)地方(接收器),那么就這樣做。 * (實(shí)際上,在這一點(diǎn)上可能總是如此。) */ if (sendTuples) { /* * If we are not able to send the tuple, we assume the destination * has closed and no more tuples can be sent. If that's the case, * end the loop. * 如果不能發(fā)送元組,有理由假設(shè)目的接收器已經(jīng)關(guān)閉,不能發(fā)送更多元組,結(jié)束循環(huán)。 */ if (!dest->receiveSlot(slot, dest)) break;//跳出循環(huán) } /* * Count tuples processed, if this is a SELECT. (For other operation * types, the ModifyTable plan node must count the appropriate * events.) * 如果操作類型為CMD_SELECT,則計(jì)算已處理的元組。 * (對(duì)于其他操作類型,ModifyTable plan節(jié)點(diǎn)必須統(tǒng)計(jì)合適的事件。) */ if (operation == CMD_SELECT) (estate->es_processed)++; /* * check our tuple count.. if we've processed the proper number then * quit, else loop again and process more tuples. Zero numberTuples * means no limit. * 檢查處理的元組計(jì)數(shù)… * 如果已完成處理,那么退出,否則再次循環(huán)并處理更多元組。 * 注意:numberTuples=0表示沒(méi)有限制。 */ current_tuple_count++; if (numberTuples && numberTuples == current_tuple_count) { /* * If we know we won't need to back up, we can release resources * at this point. * 不需要回溯,可以在此時(shí)釋放資源。 */ if (!(estate->es_top_eflags & EXEC_FLAG_BACKWARD)) (void) ExecShutdownNode(planstate); break; } } if (use_parallel_mode) ExitParallelMode();//退出并行模式 } /* ---------------------------------------------------------------- * ExecProcNode * * Execute the given node to return a(nother) tuple. * 調(diào)用node->ExecProcNode函數(shù)返回元組(one or another) * ---------------------------------------------------------------- */ #ifndef FRONTEND static inline TupleTableSlot * ExecProcNode(PlanState *node) { if (node->chgParam != NULL) /* 參數(shù)變化?something changed? */ ExecReScan(node); /* 調(diào)用ExecReScan函數(shù);let ReScan handle this */ return node->ExecProcNode(node);//執(zhí)行ExecProcNode } #endif
ExecSeqScan
ExecSeqScan函數(shù)順序掃描relation,返回下一個(gè)符合條件的元組。
/* ---------------------------------------------------------------- * ExecSeqScan(node) * * Scans the relation sequentially and returns the next qualifying * tuple. * We call the ExecScan() routine and pass it the appropriate * access method functions. * 順序掃描relation,返回下一個(gè)符合條件的元組。 * 調(diào)用ExecScan函數(shù),傳入相應(yīng)的訪問(wèn)方法函數(shù) * ---------------------------------------------------------------- */ static TupleTableSlot * ExecSeqScan(PlanState *pstate) { SeqScanState *node = castNode(SeqScanState, pstate);//獲取SeqScanState return ExecScan(&node->ss, (ExecScanAccessMtd) SeqNext, (ExecScanRecheckMtd) SeqRecheck);//執(zhí)行Scan } /* ---------------------------------------------------------------- * ExecScan * * Scans the relation using the 'access method' indicated and * returns the next qualifying tuple in the direction specified * in the global variable ExecDirection. * The access method returns the next tuple and ExecScan() is * responsible for checking the tuple returned against the qual-clause. * 使用指定的“訪問(wèn)方法”掃描關(guān)系,并按照全局變量ExecDirection中指定的方向返回下一個(gè)符合條件的元組。 * 訪問(wèn)方法返回下一個(gè)元組,ExecScan()負(fù)責(zé)根據(jù)qual-clause條件子句檢查返回的元組是否符合條件。 * * A 'recheck method' must also be provided that can check an * arbitrary tuple of the relation against any qual conditions * that are implemented internal to the access method. * 調(diào)用者還必須提供“recheck method”,根據(jù)訪問(wèn)方法內(nèi)部實(shí)現(xiàn)的條件檢查關(guān)系的所有元組。 * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * 前提條件: * 由AMI負(fù)責(zé)維護(hù)的游標(biāo)已由先前的處理過(guò)程定位. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * 初始狀態(tài): * 在游標(biāo)可定位返回第一個(gè)符合條件的元組前,relation已打開(kāi)可進(jìn)行掃描 * ---------------------------------------------------------------- */ TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, /* 返回元組的訪問(wèn)方法;function returning a tuple */ ExecScanRecheckMtd recheckMtd) //recheck方法 { ExprContext *econtext;//表達(dá)式上下文 ExprState *qual;//表達(dá)式狀態(tài) ProjectionInfo *projInfo;//投影信息 /* * Fetch data from node * 從node中提取數(shù)據(jù) */ qual = node->ps.qual; projInfo = node->ps.ps_ProjInfo; econtext = node->ps.ps_ExprContext; /* interrupt checks are in ExecScanFetch */ //在ExecScanFetch中有中斷檢查 /* * If we have neither a qual to check nor a projection to do, just skip * all the overhead and return the raw scan tuple. * 如果既沒(méi)有要檢查的條件qual,也沒(méi)有要做的投影操作,那么就跳過(guò)所有的操作并返回raw scan元組。 */ if (!qual && !projInfo) { ResetExprContext(econtext); return ExecScanFetch(node, accessMtd, recheckMtd); } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. * 重置每個(gè)元組內(nèi)存上下文,以釋放用于在前一個(gè)元組循環(huán)中分配的表達(dá)式求值內(nèi)存空間。 */ ResetExprContext(econtext); /* * get a tuple from the access method. Loop until we obtain a tuple that * passes the qualification. * 從訪問(wèn)方法中獲取一個(gè)元組。循環(huán),直到獲得通過(guò)限定條件的元組。 */ for (;;) { TupleTableSlot *slot;//slot變量 slot = ExecScanFetch(node, accessMtd, recheckMtd);//獲取slot /* * if the slot returned by the accessMtd contains NULL, then it means * there is nothing more to scan so we just return an empty slot, * being careful to use the projection result slot so it has correct * tupleDesc. * 如果accessMtd方法返回的slot中包含NULL,那么這意味著不再需要掃描了, * 這時(shí)候只需要返回一個(gè)空slot,小心使用投影結(jié)果slot,這樣可以有正確的tupleDesc了。 */ if (TupIsNull(slot)) { if (projInfo) return ExecClearTuple(projInfo->pi_state.resultslot); else return slot; } /* * place the current tuple into the expr context * 把當(dāng)前tuple放入到expr上下文中 */ econtext->ecxt_scantuple = slot; /* * check that the current tuple satisfies the qual-clause * 檢查當(dāng)前的tuple是否符合qual-clause條件 * * check for non-null qual here to avoid a function call to ExecQual() * when the qual is null ... saves only a few cycles, but they add up * ... * 在這里檢查qual是否非空,以避免在qual為空時(shí)調(diào)用ExecQual()函數(shù)… * 只節(jié)省了幾個(gè)調(diào)用周期,但它們加起來(lái)……的成本還是蠻可觀的 */ if (qual == NULL || ExecQual(qual, econtext)) { /* * Found a satisfactory scan tuple. * 發(fā)現(xiàn)一個(gè)滿足條件的元組 */ if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it. * 構(gòu)造一個(gè)投影元組,存儲(chǔ)在結(jié)果元組slot中并返回 */ return ExecProject(projInfo);//執(zhí)行投影操作并返回 } else { /* * Here, we aren't projecting, so just return scan tuple. * 不需要執(zhí)行投影操作,返回元組 */ return slot;//直接返回 } } else InstrCountFiltered1(node, 1);//instrument計(jì)數(shù) /* * Tuple fails qual, so free per-tuple memory and try again. * 元組不滿足條件,釋放資源,重試 */ ResetExprContext(econtext); } } /* * ExecScanFetch -- check interrupts & fetch next potential tuple * ExecScanFetch -- 檢查中斷&提前下一個(gè)備選元組 * * This routine is concerned with substituting a test tuple if we are * inside an EvalPlanQual recheck. If we aren't, just execute * the access method's next-tuple routine. * 這個(gè)例程是處理測(cè)試元組的替換(如果在EvalPlanQual重新檢查中)。 * 如果不是在EvalPlanQual中,則執(zhí)行access方法的next-tuple例程。 */ static inline TupleTableSlot * ExecScanFetch(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd) { EState *estate = node->ps.state; CHECK_FOR_INTERRUPTS();//檢查中斷 if (estate->es_epqTuple != NULL)//如es_epqTuple不為NULL() { //es_epqTuple字段用于在READ COMMITTED模式中替換更新后的元組后,重新評(píng)估是否滿足執(zhí)行計(jì)劃的條件quals /* * We are inside an EvalPlanQual recheck. Return the test tuple if * one is available, after rechecking any access-method-specific * conditions. * 我們正在EvalPlanQual復(fù)查。 * 如果test tuple可用,則在重新檢查所有特定于訪問(wèn)方法的條件后返回該元組。 */ Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;//訪問(wèn)的relid if (scanrelid == 0)//relid==0 { TupleTableSlot *slot = node->ss_ScanTupleSlot; /* * This is a ForeignScan or CustomScan which has pushed down a * join to the remote side. The recheck method is responsible not * only for rechecking the scan/join quals but also for storing * the correct tuple in the slot. * 這是一個(gè)ForeignScan或CustomScan,它將下推到遠(yuǎn)程端。 * recheck方法不僅負(fù)責(zé)重新檢查掃描/連接quals,還負(fù)責(zé)在slot中存儲(chǔ)正確的元組。 */ if (!(*recheckMtd) (node, slot)) ExecClearTuple(slot); /* 驗(yàn)證不通過(guò),釋放資源,不返回元組;would not be returned by scan */ return slot; } else if (estate->es_epqTupleSet[scanrelid - 1])//從estate->es_epqTupleSet數(shù)組中獲取標(biāo)志 { TupleTableSlot *slot = node->ss_ScanTupleSlot;//獲取slot /* Return empty slot if we already returned a tuple */ //如已返回元組,則清空slot if (estate->es_epqScanDone[scanrelid - 1]) return ExecClearTuple(slot); /* Else mark to remember that we shouldn't return more */ //否則,標(biāo)記沒(méi)有返回 estate->es_epqScanDone[scanrelid - 1] = true; /* Return empty slot if we haven't got a test tuple */ //如test tuple為NULL,則清空slot if (estate->es_epqTuple[scanrelid - 1] == NULL) return ExecClearTuple(slot); /* Store test tuple in the plan node's scan slot */ //在計(jì)劃節(jié)點(diǎn)的scan slot中存儲(chǔ)test tuple ExecStoreHeapTuple(estate->es_epqTuple[scanrelid - 1], slot, false); /* Check if it meets the access-method conditions */ //檢查是否滿足訪問(wèn)方法條件 if (!(*recheckMtd) (node, slot)) ExecClearTuple(slot); /* 不滿足,清空slot;would not be returned by scan */ return slot; } } /* * Run the node-type-specific access method function to get the next tuple * 運(yùn)行node-type-specific方法函數(shù),獲取下一個(gè)tuple */ return (*accessMtd) (node); } /* * ExecProject * * Projects a tuple based on projection info and stores it in the slot passed * to ExecBuildProjectInfo(). * 根據(jù)投影信息投影一個(gè)元組,并將其存儲(chǔ)在傳遞給ExecBuildProjectInfo()的slot中。 * * Note: the result is always a virtual tuple; therefore it may reference * the contents of the exprContext's scan tuples and/or temporary results * constructed in the exprContext. If the caller wishes the result to be * valid longer than that data will be valid, he must call ExecMaterializeSlot * on the result slot. * 注意:結(jié)果總是一個(gè)虛擬元組; * 因此,它可以引用exprContext的掃描元組和/或exprContext中構(gòu)造的臨時(shí)結(jié)果的內(nèi)容。 * 如果調(diào)用者希望結(jié)果有效的時(shí)間長(zhǎng)于數(shù)據(jù)有效的時(shí)間,必須在結(jié)果slot上調(diào)用ExecMaterializeSlot。 */ #ifndef FRONTEND static inline TupleTableSlot * ExecProject(ProjectionInfo *projInfo) { ExprContext *econtext = projInfo->pi_exprContext; ExprState *state = &projInfo->pi_state; TupleTableSlot *slot = state->resultslot; bool isnull; /* * Clear any former contents of the result slot. This makes it safe for * us to use the slot's Datum/isnull arrays as workspace. * 清除以前的結(jié)果slot內(nèi)容。 * 這使得我們可以安全地使用slot的Datum/isnull數(shù)組作為工作區(qū)。 */ ExecClearTuple(slot); /* Run the expression, discarding scalar result from the last column. */ //運(yùn)行表達(dá)式,從最后一列丟棄scalar結(jié)果。 (void) ExecEvalExprSwitchContext(state, econtext, &isnull); /* * Successfully formed a result row. Mark the result slot as containing a * valid virtual tuple (inlined version of ExecStoreVirtualTuple()). * 成功形成了一個(gè)結(jié)果行。 * 將結(jié)果slot標(biāo)記為包含一個(gè)有效的虛擬元組(ExecStoreVirtualTuple()的內(nèi)聯(lián)版本)。 */ slot->tts_flags &= ~TTS_FLAG_EMPTY; slot->tts_nvalid = slot->tts_tupleDescriptor->natts; return slot; } #endif /* * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via * ExecPrepareQual). Returns true if qual is satisfied, else false. * 解析用ExecInitQual準(zhǔn)備的條件qual(可能通過(guò)ExecPrepareQual)。 * 如果滿足條件qual,返回true,否則為false。 * * Note: ExecQual used to have a third argument "resultForNull". The * behavior of this function now corresponds to resultForNull == false. * If you want the resultForNull == true behavior, see ExecCheck. * 注意:ExecQual曾經(jīng)有第三個(gè)參數(shù)“resultForNull”。 * 這個(gè)函數(shù)的行為現(xiàn)在對(duì)應(yīng)于resultForNull == false。 * 如果希望resultForNull == true行為,請(qǐng)參閱ExecCheck。 */ #ifndef FRONTEND static inline bool ExecQual(ExprState *state, ExprContext *econtext) { Datum ret; bool isnull; /* short-circuit (here and in ExecInitQual) for empty restriction list */ //如state為NULL,直接返回 if (state == NULL) return true; /* verify that expression was compiled using ExecInitQual */ //使用函數(shù)ExecInitQual驗(yàn)證表達(dá)式是否可以編譯 Assert(state->flags & EEO_FLAG_IS_QUAL); ret = ExecEvalExprSwitchContext(state, econtext, &isnull); /* EEOP_QUAL不應(yīng)返回NULL;EEOP_QUAL should never return NULL */ Assert(!isnull); return DatumGetBool(ret); } #endif /* -------------------------------- * ExecClearTuple * * This function is used to clear out a slot in the tuple table. * 該函數(shù)清空tuple table中的slot * NB: only the tuple is cleared, not the tuple descriptor (if any). * 注意:只有tuple被清除,而不是tuple描述符 * -------------------------------- */ TupleTableSlot * /* 返回驗(yàn)證通過(guò)的slot;return: slot passed */ ExecClearTuple(TupleTableSlot *slot) /* 存儲(chǔ)tuple的slot;slot in which to store tuple */ { /* * sanity checks * 安全檢查 */ Assert(slot != NULL); /* * Free the old physical tuple if necessary. * 如需要,釋放原有的物理元組 */ if (TTS_SHOULDFREE(slot)) { heap_freetuple(slot->tts_tuple);//釋放元組 slot->tts_flags &= ~TTS_FLAG_SHOULDFREE; } if (TTS_SHOULDFREEMIN(slot)) { heap_free_minimal_tuple(slot->tts_mintuple); slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN; } slot->tts_tuple = NULL;//設(shè)置NULL值 slot->tts_mintuple = NULL; /* * Drop the pin on the referenced buffer, if there is one. * 如果有的話,將pin放在已引用的緩沖區(qū)上。 */ if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer);//釋放緩沖區(qū) slot->tts_buffer = InvalidBuffer; /* * Mark it empty. * 標(biāo)記為空 */ slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_nvalid = 0; return slot; }
測(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-# 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)
啟動(dòng)gdb,設(shè)置斷點(diǎn),進(jìn)入ExecutePlan
(gdb) b ExecutePlan Breakpoint 1 at 0x6db79d: file execMain.c, line 1694. (gdb) c Continuing. Breakpoint 1, ExecutePlan (estate=0x14daf48, planstate=0x14db160, use_parallel_mode=false, operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x14d9ed0, execute_once=true) at execMain.c:1694 warning: Source file is more recent than executable. 1694 current_tuple_count = 0;
查看輸入?yún)?shù)
planstate->type:T_SortState->排序Plan
planstate->ExecProcNode:ExecProcNodeFirst,封裝器
planstate->ExecProcNodeReal:ExecSort,實(shí)際的函數(shù)
use_parallel_mode:false,非并行模式
operation:CMD_SELECT,查詢操作
sendTuples:T,需要發(fā)送元組給客戶端
numberTuples:0,所有元組
direction:ForwardScanDirection
dest:printtup(console客戶端)
execute_once:T,只執(zhí)行一次
(gdb) p *estate $1 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x1493e10, es_crosscheck_snapshot = 0x0, es_range_table = 0x14d7c00, es_plannedstmt = 0x14d9d58, es_sourceText = 0x13eeeb8 "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 = 0x14dae30, es_tupleTable = 0x14dbaf8, es_rowMarks = 0x0, es_processed = 0, es_lastoid = 0, es_top_eflags = 16, es_instrument = 0, es_finished = false, es_exprcontexts = 0x14db550, 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) p *planstate $2 = {type = T_SortState, plan = 0x14d3f90, state = 0x14daf48, ExecProcNode = 0x6e41bb <ExecProcNodeFirst>, ExecProcNodeReal = 0x716144 <ExecSort>, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x14db278, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x14ec470, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x14e9fd0} (gdb) p *dest $4 = {receiveSlot = 0x48cc00 <printtup>, rStartup = 0x48c5c1 <printtup_startup>, rShutdown = 0x48d02e <printtup_shutdown>, rDestroy = 0x48d0a7 <printtup_destroy>, mydest = DestRemote}
賦值,準(zhǔn)備執(zhí)行ExecProcNode(ExecSort)
(gdb) n 1699 estate->es_direction = direction; (gdb) 1705 if (!execute_once) (gdb) 1708 estate->es_use_parallel_mode = use_parallel_mode; (gdb) 1709 if (use_parallel_mode) (gdb) 1718 ResetPerTupleExprContext(estate); (gdb) 1723 slot = ExecProcNode(planstate); (gdb)
執(zhí)行ExecProcNode(ExecSort),返回slot
(gdb) 1729 if (TupIsNull(slot)) (gdb) p *slot $5 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x14ec4b0, tts_tupleDescriptor = 0x14ec058, tts_mcxt = 0x14dae30, tts_buffer = 0, tts_nvalid = 0, tts_values = 0x14ec4d0, tts_isnull = 0x14ec508, tts_mintuple = 0x1a4b078, tts_minhdr = {t_len = 64, t_self = {ip_blkid = { bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x1a4b070}, tts_off = 0, tts_fixedTupleDescriptor = true}
查看slot中的數(shù)據(jù)
注意:slot中的t_data不是實(shí)際的tuple data,而是緩沖區(qū)信息,在返回時(shí)根據(jù)這些信息從緩沖區(qū)獲取數(shù)據(jù)返回
(gdb) p *slot $5 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x14ec4b0, tts_tupleDescriptor = 0x14ec058, tts_mcxt = 0x14dae30, tts_buffer = 0, tts_nvalid = 0, tts_values = 0x14ec4d0, tts_isnull = 0x14ec508, tts_mintuple = 0x1a4b078, tts_minhdr = {t_len = 64, t_self = {ip_blkid = { bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x1a4b070}, tts_off = 0, tts_fixedTupleDescriptor = true} (gdb) p *slot->tts_tuple $6 = {t_len = 64, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x1a4b070} (gdb) p *slot->tts_tuple->t_data $7 = {t_choice = {t_heap = {t_xmin = 21967600, t_xmax = 0, t_field3 = {t_cid = 56, t_xvac = 56}}, t_datum = { datum_len_ = 21967600, datum_typmod = 0, datum_typeid = 56}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 32639}, t_infomask2 = 7, t_infomask = 2, t_hoff = 24 '\030', t_bits = 0x1a4b087 ""}
判斷是否需要過(guò)濾屬性(不需要)
(gdb) n 1748 if (estate->es_junkFilter != NULL) (gdb) (gdb) p estate->es_junkFilter $12 = (JunkFilter *) 0x0
修改計(jì)數(shù)器等信息
(gdb) 1755 if (sendTuples) (gdb) 1762 if (!dest->receiveSlot(slot, dest)) (gdb) 1771 if (operation == CMD_SELECT) (gdb) 1772 (estate->es_processed)++; (gdb) p estate->es_processed $9 = 0 (gdb) n 1779 current_tuple_count++; (gdb) p current_tuple_count $10 = 0 (gdb) n 1780 if (numberTuples && numberTuples == current_tuple_count) (gdb) p numberTuples $11 = 0 (gdb) n 1790 }
繼續(xù)循環(huán),直接滿足條件(全部掃描完畢)未知
(gdb) n 1718 ResetPerTupleExprContext(estate); (gdb) 1723 slot = ExecProcNode(planstate); (gdb) 1729 if (TupIsNull(slot)) ...
ExecutePlan的主體邏輯已介紹完畢,下面簡(jiǎn)單跟蹤分析ExecSeqScan函數(shù)
設(shè)置斷點(diǎn),進(jìn)入ExecSeqScan
(gdb) del 1 (gdb) c Continuing. Breakpoint 2, ExecSeqScan (pstate=0x14e99a0) at nodeSeqscan.c:127 warning: Source file is more recent than executable. 127 SeqScanState *node = castNode(SeqScanState, pstate);
查看輸入?yún)?shù)
plan為SeqScan
ExecProcNode=ExecProcNodeReal,均為函數(shù)ExecSeqScan
targetlist為投影列信息
(gdb) p *pstate $13 = {type = T_SeqScanState, plan = 0x14d5570, state = 0x14daf48, ExecProcNode = 0x714d59 <ExecSeqScan>, ExecProcNodeReal = 0x714d59 <ExecSeqScan>, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x14e9c38, ps_ExprContext = 0x14e9ab8, ps_ProjInfo = 0x0, scandesc = 0x7fa45b442ab8} (gdb) p *pstate->plan $14 = {type = T_SeqScan, startup_cost = 0, total_cost = 164, plan_rows = 10000, plan_width = 20, parallel_aware = false, parallel_safe = true, plan_node_id = 7, targetlist = 0x14d5438, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
進(jìn)入ExecScan函數(shù)
accessMtd方法為SeqNext
recheckMtd方法為SeqRecheck
(gdb) n 129 return ExecScan(&node->ss, (gdb) step ExecScan (node=0x14e99a0, accessMtd=0x714c6d <SeqNext>, recheckMtd=0x714d3d <SeqRecheck>) at execScan.c:132 warning: Source file is more recent than executable. 132 qual = node->ps.qual;
ExecScan->投影信息,為NULL
(gdb) p *projInfo Cannot access memory at address 0x0
ExecScan->約束條件為NULL
(gdb) p *qual Cannot access memory at address 0x0
ExecScan->如果既沒(méi)有要檢查的條件qual,也沒(méi)有要做的投影操作,那么就跳過(guò)所有的操作并返回raw scan元組
(gdb) n 142 if (!qual && !projInfo) (gdb) 144 ResetExprContext(econtext); (gdb) n 145 return ExecScanFetch(node, accessMtd, recheckMtd);
ExecScan->進(jìn)入ExecScanFetch
(gdb) step ExecScanFetch (node=0x14e99a0, accessMtd=0x714c6d <SeqNext>, recheckMtd=0x714d3d <SeqRecheck>) at execScan.c:39 39 EState *estate = node->ps.state;
ExecScan->檢查中斷,判斷是否處于EvalPlanQual recheck狀態(tài)(為NULL,實(shí)際不是)
39 EState *estate = node->ps.state; (gdb) n 41 CHECK_FOR_INTERRUPTS(); (gdb) 43 if (estate->es_epqTuple != NULL) (gdb) p *estate->es_epqTuple Cannot access memory at address 0x0
ExecScan->調(diào)用訪問(wèn)方法SeqNext,返回slot
(gdb) n 95 return (*accessMtd) (node); (gdb) n 96 }
ExecScan->回到ExecScan&ExecSeqScan,結(jié)束調(diào)用
(gdb) n ExecScan (node=0x14e99a0, accessMtd=0x714c6d <SeqNext>, recheckMtd=0x714d3d <SeqRecheck>) at execScan.c:219 219 } (gdb) ExecSeqScan (pstate=0x14e99a0) at nodeSeqscan.c:132 132 } (gdb)
到此,關(guān)于“PostgreSQL中ExecutePlan函數(shù)與ExecSeqScan函數(shù)的作用是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。