您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中ExecutePlan函數與ExecSeqScan函數的作用是什么”,在日常操作中,相信很多人在PostgreSQL中ExecutePlan函數與ExecSeqScan函數的作用是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中ExecutePlan函數與ExecSeqScan函數的作用是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
ExecutePlan函數處理查詢計劃,直到檢索到指定數量(參數numbertuple)的元組,并沿著指定的方向掃描。ExecSeqScan函數順序掃描relation,返回下一個符合條件的元組。
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;
ExecutePlan
PortalRunSelect->ExecutorRun->ExecutePlan函數處理查詢計劃,直到檢索到指定數量(參數numbertuple)的元組,并沿著指定的方向掃描.
/* ---------------------------------------------------------------- * ExecutePlan * * Processes the query plan until we have retrieved 'numberTuples' tuples, * moving in the specified direction. * 處理查詢計劃,直到檢索到指定數量(參數numbertuple)的元組,并沿著指定的方向移動。 * * Runs to completion if numberTuples is 0 * 如參數numbertuple為0,則運行至結束為止 * * Note: the ctid attribute is a 'junk' attribute that is removed before the * user can see it * 注意:ctid屬性是"junk"屬性,在返回給用戶前會移除 * ---------------------------------------------------------------- */ static void ExecutePlan(EState *estate,//執行狀態 PlanState *planstate,//計劃狀態 bool use_parallel_mode,//是否使用并行模式 CmdType operation,//操作類型 bool sendTuples,//是否需要傳輸元組 uint64 numberTuples,//元組數量 ScanDirection direction,//掃描方向 DestReceiver *dest,//接收的目標端 bool execute_once)//是否只執行一次 { TupleTableSlot *slot;//元組表Slot uint64 current_tuple_count;//當前的元組計數 /* * initialize local variables * 初始化本地變量 */ current_tuple_count = 0; /* * Set the direction. * 設置掃描方向 */ 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. * 如果計劃可能被多次執行,那么必須強制它在非并行的情況下運行,因為可能會提前退出。 */ if (!execute_once) use_parallel_mode = false;//如需多次執行,則不允許并行執行 estate->es_use_parallel_mode = use_parallel_mode; if (use_parallel_mode) EnterParallelMode();//如并行,則進入并行模式 /* * Loop until we've processed the proper number of tuples from the plan. * 循環直至執行計劃已處理完成相應數量的元組 * 注意:每次循環只處理一個元組,每次都要重置元組Expr的上下文/過濾不需要的列/發送元組 */ for (;;) { /* Reset the per-output-tuple exprcontext */ //重置Expr上下文 ResetPerTupleExprContext(estate); /* * Execute the plan and obtain a tuple * 執行計劃,獲取一個元組 */ slot = ExecProcNode(planstate); /* * if the tuple is null, then we assume there is nothing more to * process so we just end the loop... * 如果返回的元組為空,那么可以認為沒有什么要處理的了,結束循環…… */ 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過濾器,使用junk執行投影操作,產生一個新的元組 * * 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.) * 將這個新的“clean”元組存儲在junkfilter的resultSlot中。 * (以前,將其存儲在“dirty” tuple上,這是錯誤的,因為該tuple slot的描述符是錯誤的。) */ 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.) * 如果要將元組發送到某個地方(接收器),那么就這樣做。 * (實際上,在這一點上可能總是如此。) */ 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. * 如果不能發送元組,有理由假設目的接收器已經關閉,不能發送更多元組,結束循環。 */ if (!dest->receiveSlot(slot, dest)) break;//跳出循環 } /* * Count tuples processed, if this is a SELECT. (For other operation * types, the ModifyTable plan node must count the appropriate * events.) * 如果操作類型為CMD_SELECT,則計算已處理的元組。 * (對于其他操作類型,ModifyTable plan節點必須統計合適的事件。) */ 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. * 檢查處理的元組計數… * 如果已完成處理,那么退出,否則再次循環并處理更多元組。 * 注意:numberTuples=0表示沒有限制。 */ 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. * 不需要回溯,可以在此時釋放資源。 */ 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. * 調用node->ExecProcNode函數返回元組(one or another) * ---------------------------------------------------------------- */ #ifndef FRONTEND static inline TupleTableSlot * ExecProcNode(PlanState *node) { if (node->chgParam != NULL) /* 參數變化?something changed? */ ExecReScan(node); /* 調用ExecReScan函數;let ReScan handle this */ return node->ExecProcNode(node);//執行ExecProcNode } #endif
ExecSeqScan
ExecSeqScan函數順序掃描relation,返回下一個符合條件的元組。
/* ---------------------------------------------------------------- * 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,返回下一個符合條件的元組。 * 調用ExecScan函數,傳入相應的訪問方法函數 * ---------------------------------------------------------------- */ static TupleTableSlot * ExecSeqScan(PlanState *pstate) { SeqScanState *node = castNode(SeqScanState, pstate);//獲取SeqScanState return ExecScan(&node->ss, (ExecScanAccessMtd) SeqNext, (ExecScanRecheckMtd) SeqRecheck);//執行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. * 使用指定的“訪問方法”掃描關系,并按照全局變量ExecDirection中指定的方向返回下一個符合條件的元組。 * 訪問方法返回下一個元組,ExecScan()負責根據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. * 調用者還必須提供“recheck method”,根據訪問方法內部實現的條件檢查關系的所有元組。 * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * 前提條件: * 由AMI負責維護的游標已由先前的處理過程定位. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * 初始狀態: * 在游標可定位返回第一個符合條件的元組前,relation已打開可進行掃描 * ---------------------------------------------------------------- */ TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, /* 返回元組的訪問方法;function returning a tuple */ ExecScanRecheckMtd recheckMtd) //recheck方法 { ExprContext *econtext;//表達式上下文 ExprState *qual;//表達式狀態 ProjectionInfo *projInfo;//投影信息 /* * Fetch data from node * 從node中提取數據 */ 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. * 如果既沒有要檢查的條件qual,也沒有要做的投影操作,那么就跳過所有的操作并返回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. * 重置每個元組內存上下文,以釋放用于在前一個元組循環中分配的表達式求值內存空間。 */ ResetExprContext(econtext); /* * get a tuple from the access method. Loop until we obtain a tuple that * passes the qualification. * 從訪問方法中獲取一個元組。循環,直到獲得通過限定條件的元組。 */ 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,那么這意味著不再需要掃描了, * 這時候只需要返回一個空slot,小心使用投影結果slot,這樣可以有正確的tupleDesc了。 */ if (TupIsNull(slot)) { if (projInfo) return ExecClearTuple(projInfo->pi_state.resultslot); else return slot; } /* * place the current tuple into the expr context * 把當前tuple放入到expr上下文中 */ econtext->ecxt_scantuple = slot; /* * check that the current tuple satisfies the qual-clause * 檢查當前的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為空時調用ExecQual()函數… * 只節省了幾個調用周期,但它們加起來……的成本還是蠻可觀的 */ if (qual == NULL || ExecQual(qual, econtext)) { /* * Found a satisfactory scan tuple. * 發現一個滿足條件的元組 */ if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it. * 構造一個投影元組,存儲在結果元組slot中并返回 */ return ExecProject(projInfo);//執行投影操作并返回 } else { /* * Here, we aren't projecting, so just return scan tuple. * 不需要執行投影操作,返回元組 */ return slot;//直接返回 } } else InstrCountFiltered1(node, 1);//instrument計數 /* * Tuple fails qual, so free per-tuple memory and try again. * 元組不滿足條件,釋放資源,重試 */ ResetExprContext(econtext); } } /* * ExecScanFetch -- check interrupts & fetch next potential tuple * ExecScanFetch -- 檢查中斷&提前下一個備選元組 * * 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. * 這個例程是處理測試元組的替換(如果在EvalPlanQual重新檢查中)。 * 如果不是在EvalPlanQual中,則執行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模式中替換更新后的元組后,重新評估是否滿足執行計劃的條件quals /* * We are inside an EvalPlanQual recheck. Return the test tuple if * one is available, after rechecking any access-method-specific * conditions. * 我們正在EvalPlanQual復查。 * 如果test tuple可用,則在重新檢查所有特定于訪問方法的條件后返回該元組。 */ Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;//訪問的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. * 這是一個ForeignScan或CustomScan,它將下推到遠程端。 * recheck方法不僅負責重新檢查掃描/連接quals,還負責在slot中存儲正確的元組。 */ if (!(*recheckMtd) (node, slot)) ExecClearTuple(slot); /* 驗證不通過,釋放資源,不返回元組;would not be returned by scan */ return slot; } else if (estate->es_epqTupleSet[scanrelid - 1])//從estate->es_epqTupleSet數組中獲取標志 { 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 */ //否則,標記沒有返回 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 */ //在計劃節點的scan slot中存儲test tuple ExecStoreHeapTuple(estate->es_epqTuple[scanrelid - 1], slot, false); /* Check if it meets the access-method conditions */ //檢查是否滿足訪問方法條件 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 * 運行node-type-specific方法函數,獲取下一個tuple */ return (*accessMtd) (node); } /* * ExecProject * * Projects a tuple based on projection info and stores it in the slot passed * to ExecBuildProjectInfo(). * 根據投影信息投影一個元組,并將其存儲在傳遞給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. * 注意:結果總是一個虛擬元組; * 因此,它可以引用exprContext的掃描元組和/或exprContext中構造的臨時結果的內容。 * 如果調用者希望結果有效的時間長于數據有效的時間,必須在結果slot上調用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. * 清除以前的結果slot內容。 * 這使得我們可以安全地使用slot的Datum/isnull數組作為工作區。 */ ExecClearTuple(slot); /* Run the expression, discarding scalar result from the last column. */ //運行表達式,從最后一列丟棄scalar結果。 (void) ExecEvalExprSwitchContext(state, econtext, &isnull); /* * Successfully formed a result row. Mark the result slot as containing a * valid virtual tuple (inlined version of ExecStoreVirtualTuple()). * 成功形成了一個結果行。 * 將結果slot標記為包含一個有效的虛擬元組(ExecStoreVirtualTuple()的內聯版本)。 */ 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準備的條件qual(可能通過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曾經有第三個參數“resultForNull”。 * 這個函數的行為現在對應于resultForNull == false。 * 如果希望resultForNull == true行為,請參閱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 */ //使用函數ExecInitQual驗證表達式是否可以編譯 Assert(state->flags & EEO_FLAG_IS_QUAL); ret = ExecEvalExprSwitchContext(state, econtext, &isnull); /* EEOP_QUAL不應返回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. * 該函數清空tuple table中的slot * NB: only the tuple is cleared, not the tuple descriptor (if any). * 注意:只有tuple被清除,而不是tuple描述符 * -------------------------------- */ TupleTableSlot * /* 返回驗證通過的slot;return: slot passed */ ExecClearTuple(TupleTableSlot *slot) /* 存儲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;//設置NULL值 slot->tts_mintuple = NULL; /* * Drop the pin on the referenced buffer, if there is one. * 如果有的話,將pin放在已引用的緩沖區上。 */ if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer);//釋放緩沖區 slot->tts_buffer = InvalidBuffer; /* * Mark it empty. * 標記為空 */ slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_nvalid = 0; return slot; }
測試腳本如下
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,設置斷點,進入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;
查看輸入參數
planstate->type:T_SortState->排序Plan
planstate->ExecProcNode:ExecProcNodeFirst,封裝器
planstate->ExecProcNodeReal:ExecSort,實際的函數
use_parallel_mode:false,非并行模式
operation:CMD_SELECT,查詢操作
sendTuples:T,需要發送元組給客戶端
numberTuples:0,所有元組
direction:ForwardScanDirection
dest:printtup(console客戶端)
execute_once:T,只執行一次
(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}
賦值,準備執行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)
執行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中的數據
注意:slot中的t_data不是實際的tuple data,而是緩沖區信息,在返回時根據這些信息從緩沖區獲取數據返回
(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 ""}
判斷是否需要過濾屬性(不需要)
(gdb) n 1748 if (estate->es_junkFilter != NULL) (gdb) (gdb) p estate->es_junkFilter $12 = (JunkFilter *) 0x0
修改計數器等信息
(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 }
繼續循環,直接滿足條件(全部掃描完畢)未知
(gdb) n 1718 ResetPerTupleExprContext(estate); (gdb) 1723 slot = ExecProcNode(planstate); (gdb) 1729 if (TupIsNull(slot)) ...
ExecutePlan的主體邏輯已介紹完畢,下面簡單跟蹤分析ExecSeqScan函數
設置斷點,進入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);
查看輸入參數
plan為SeqScan
ExecProcNode=ExecProcNodeReal,均為函數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}
進入ExecScan函數
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->如果既沒有要檢查的條件qual,也沒有要做的投影操作,那么就跳過所有的操作并返回raw scan元組
(gdb) n 142 if (!qual && !projInfo) (gdb) 144 ResetExprContext(econtext); (gdb) n 145 return ExecScanFetch(node, accessMtd, recheckMtd);
ExecScan->進入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狀態(為NULL,實際不是)
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->調用訪問方法SeqNext,返回slot
(gdb) n 95 return (*accessMtd) (node); (gdb) n 96 }
ExecScan->回到ExecScan&ExecSeqScan,結束調用
(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)
到此,關于“PostgreSQL中ExecutePlan函數與ExecSeqScan函數的作用是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。