PostgreSQL 原始碼解讀(7)- 插入資料#6(ExecProcNode和ExecPro...
本文簡單介紹了PG插入資料部分的原始碼,主要內容包括ExecProcNode和ExecProcNodeFirst函式的實現邏輯,ExecProcNode函式位於executor.h檔案中,ExecProcNodeFirst函式位於execProcnode.c檔案中。
一、基礎資訊
ExecProcNode/ExecProcNodeFirst函式使用的資料結構、宏定義以及依賴的函式等。
資料結構/宏定義
1、ExecProcNodeMtd
ExecProcNodeMtd是一個函式指標型別,指向的函式輸入引數是PlanState結構體指標,輸出引數是TupleTableSlot 結構體指標
/* ----------------
* ExecProcNodeMtd
*
* This is the method called by ExecProcNode to return the next tuple
* from an executor node. It returns NULL, or an empty TupleTableSlot,
* if no more tuples are available.
* ----------------
*/
typedef TupleTableSlot *(*ExecProcNodeMtd) (struct PlanState *pstate);
依賴的函式
1、check_stack_depth
//檢查stack的深度,如超出系統限制,則主動報錯
/*
* check_stack_depth/stack_is_too_deep: check for excessively deep recursion
*
* This should be called someplace in any recursive routine that might possibly
* recurse deep enough to overflow the stack. Most Unixen treat stack
* overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
* before hitting the hardware limit.
*
* check_stack_depth() just throws an error summarily. stack_is_too_deep()
* can be used by code that wants to handle the error condition itself.
*/
void
check_stack_depth(void)
{
if (stack_is_too_deep())
{
ereport(ERROR,
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
errmsg("stack depth limit exceeded"),
errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
"after ensuring the platform's stack depth limit is adequate.",
max_stack_depth)));
}
}
bool
stack_is_too_deep(void)
{
char stack_top_loc;
long stack_depth;
/*
* Compute distance from reference point to my local variables
*/
stack_depth = (long) (stack_base_ptr - &stack_top_loc);
/*
* Take abs value, since stacks grow up on some machines, down on others
*/
if (stack_depth < 0)
stack_depth = -stack_depth;
/*
* Trouble?
*
* The test on stack_base_ptr prevents us from erroring out if called
* during process setup or in a non-backend process. Logically it should
* be done first, but putting it here avoids wasting cycles during normal
* cases.
*/
if (stack_depth > max_stack_depth_bytes &&
stack_base_ptr != NULL)
return true;
/*
* On IA64 there is a separate "register" stack that requires its own
* independent check. For this, we have to measure the change in the
* "BSP" pointer from PostgresMain to here. Logic is just as above,
* except that we know IA64's register stack grows up.
*
* Note we assume that the same max_stack_depth applies to both stacks.
*/
#if defined(__ia64__) || defined(__ia64)
stack_depth = (long) (ia64_get_bsp() - register_stack_base_ptr);
if (stack_depth > max_stack_depth_bytes &&
register_stack_base_ptr != NULL)
return true;
#endif /* IA64 */
return false;
}
2、ExecProcNodeInstr
/*
* ExecProcNode wrapper that performs instrumentation calls. By keeping
* this a separate function, we avoid overhead in the normal case where
* no instrumentation is wanted.
*/
static TupleTableSlot *
ExecProcNodeInstr(PlanState *node)
{
TupleTableSlot *result;
InstrStartNode(node->instrument);
result = node->ExecProcNodeReal(node);
InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
return result;
}
二、原始碼解讀
1、ExecProcNode
//外部呼叫者可透過改變node實現遍歷
/* ----------------------------------------------------------------
* ExecProcNode
*
* Execute the given node to return a(nother) tuple.
* ----------------------------------------------------------------
*/
#ifndef FRONTEND
static inline TupleTableSlot *
ExecProcNode(PlanState *node)
{
if (node->chgParam != NULL) /* something changed? */
ExecReScan(node); /* let ReScan handle this */
return node->ExecProcNode(node);
}
#endif
2、ExecProcNodeFirst
/*
* ExecProcNode wrapper that performs some one-time checks, before calling
* the relevant node method (possibly via an instrumentation wrapper).
*/
/*
輸入:
node-PlanState指標
輸出:
儲存Tuple的Slot
*/
static TupleTableSlot *
ExecProcNodeFirst(PlanState *node)
{
/*
* Perform stack depth check during the first execution of the node. We
* only do so the first time round because it turns out to not be cheap on
* some common architectures (eg. x86). This relies on the assumption
* that ExecProcNode calls for a given plan node will always be made at
* roughly the same stack depth.
*/
//檢查Stack是否超深
check_stack_depth();
/*
* If instrumentation is required, change the wrapper to one that just
* does instrumentation. Otherwise we can dispense with all wrappers and
* have ExecProcNode() directly call the relevant function from now on.
*/
//如果instrument(TODO)
if (node->instrument)
node->ExecProcNode = ExecProcNodeInstr;
else
node->ExecProcNode = node->ExecProcNodeReal;
//執行該Node的處理過程
return node->ExecProcNode(node);
}
三、跟蹤分析
插入測試資料:
testdb=# -- 獲取pid
testdb=# select pg_backend_pid();
pg_backend_pid
----------------
2835
(1 row)
testdb=# -- 插入1行
testdb=# insert into t_insert values(14,'ExecProcNodeFirst','ExecProcNodeFirst','ExecProcNodeFirst');
(掛起)
啟動gdb分析:
[root@localhost ~]# gdb -p 2835
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
Copyright (C) 2013 Free Software Foundation, Inc.
...
(gdb) b ExecProcNodeFirst
Breakpoint 1 at 0x69a797: file execProcnode.c, line 433.
(gdb) c
Continuing.
Breakpoint 1, ExecProcNodeFirst (node=0x2cca790) at execProcnode.c:433
433 check_stack_depth();
#檢視輸入引數
(gdb) p *node
$1 = {type = T_ModifyTableState, plan = 0x2c1d028, state = 0x2cca440, ExecProcNode = 0x69a78b <ExecProcNodeFirst>, ExecProcNodeReal = 0x6c2485 <ExecModifyTable>, instrument = 0x0,
worker_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2ccb6a0, ps_ExprContext = 0x0, ps_ProjInfo = 0x0,
scandesc = 0x0}
#ExecProcNode 實際對應的函式是ExecProcNodeFirst
#ExecProcNodeReal 實際對應的函式是ExecModifyTable(上一章節已粗略解析)
(gdb) next
440 if (node->instrument)
(gdb)
#實際呼叫ExecModifyTable函式(這個函式由更高層的呼叫函式植入)
443 node->ExecProcNode = node->ExecProcNodeReal;
(gdb)
445 return node->ExecProcNode(node);
(gdb) next
#第二次呼叫(TODO)
Breakpoint 1, ExecProcNodeFirst (node=0x2ccac80) at execProcnode.c:433
433 check_stack_depth();
(gdb) next
440 if (node->instrument)
(gdb) next
443 node->ExecProcNode = node->ExecProcNodeReal;
(gdb) next
445 return node->ExecProcNode(node);
(gdb) next
446 }
(gdb) next
ExecProcNode (node=0x2ccac80) at ../../../src/include/executor/executor.h:238
238 }
#第二次呼叫的引數
(gdb) p *node
$2 = {type = T_ResultState, plan = 0x2cd0488, state = 0x2cca440, ExecProcNode = 0x6c5094 <ExecResult>, ExecProcNodeReal = 0x6c5094 <ExecResult>, instrument = 0x0, worker_instrument = 0x0, qual = 0x0,
lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2ccad90, ps_ExprContext = 0x2ccab30, ps_ProjInfo = 0x2ccabc0, scandesc = 0x0}
#ExecProcNode對應的實際函式是ExecResult
(gdb)
四、小結
1、C語言中的多型:C語言中使用函式指標實現了函式的“多型”,增強了程式碼的可重用性,當然,代價是複雜度有所提升;
2、引數構造:更詳細的引數構造,需要上層呼叫的進一步解讀。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374909/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PostgreSQL 原始碼解讀(8)- 插入資料#7(ExecutePlan)SQL原始碼
- PostgreSQL 原始碼解讀(6)- 插入資料#5(ExecModifyTable)SQL原始碼
- PostgreSQL 原始碼解讀(1)- 插入資料#1SQL原始碼
- PostgreSQL 原始碼解讀(10)- 插入資料#9(ProcessQuery)SQL原始碼
- PostgreSQL 原始碼解讀(13)- 插入資料#12(PostgresMain)SQL原始碼AI
- PostgreSQL 原始碼解讀(2)- 插入資料#2(RelationPutHeapTuple)SQL原始碼APT
- PostgreSQL 原始碼解讀(9)- 插入資料#8(ExecutorRun和standard...SQL原始碼
- PostgreSQL 原始碼解讀(11)- 插入資料#10(PortalRunMulti和Por...SQL原始碼
- PostgreSQL 原始碼解讀(5)- 插入資料#4(ExecInsert)SQL原始碼
- PostgreSQL 原始碼解讀(12)- 插入資料#11(exec_simple_query)SQL原始碼
- PostgreSQL 原始碼解讀(4)- 插入資料#3(heap_insert)SQL原始碼
- PostgreSQL 原始碼解讀(92)- 分割槽表#1(資料插入路由#1)SQL原始碼路由
- PostgreSQL 原始碼解讀(94)- 分割槽表#2(資料插入路由#2)SQL原始碼路由
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- PostgreSQL 原始碼解讀(96)- 分割槽表#3(資料插入路由#3-獲取分割槽鍵值)SQL原始碼路由
- PostgreSQL 原始碼解讀(198)- 查詢#113(排序#6 - Tuplesortstate)SQL原始碼排序
- PostgreSQL 原始碼解讀(154)- 後臺程式#6(walsender#2)SQL原始碼
- PostgreSQL 原始碼解讀(241)- plpgsql(CreateFunction)SQL原始碼Function
- PostgreSQL 原始碼解讀(100)- 分割槽表#6(資料查詢路由#3-prune part...SQL原始碼路由
- PostgreSQL 原始碼解讀(155)- 後臺程式#7(walsender#3)SQL原始碼
- PostgreSQL 原始碼解讀(254)- PG 14(Improving connection scalability)#6SQL原始碼
- PostgreSQL 原始碼解讀(103)- 分割槽表#9(資料查詢路由#6-APPEND初始化和實現)SQL原始碼路由APP
- PostgreSQL 原始碼解讀(101)- 分割槽表#7(資料查詢路由#4-prune part...SQL原始碼路由
- PostgreSQL 原始碼解讀(255)- PG 14(Improving connection scalability)#7SQL原始碼
- PostgreSQL 原始碼解讀(21)- 查詢語句#6(PlannedStmt詳解-跟蹤分析)SQL原始碼
- PostgreSQL 原始碼解讀(240)- HTAB簡介SQL原始碼
- PostgreSQL 原始碼解讀(219)- Locks(Overview)SQL原始碼View
- PostgreSQL FSM(Free Space Map) 原始碼解讀SQL原始碼
- antd原始碼解讀(6)- Affix原始碼
- PostgreSQL 原始碼解讀(141)- Buffer Manager#6(Clock Sweep演算法)SQL原始碼演算法
- PostgreSQL 原始碼解讀(112)- WAL#8(XLogCtrl資料結構)SQL原始碼GC資料結構
- PostgreSQL 原始碼解讀(108)- 後臺程式#1(PGPROC資料結構)SQL原始碼資料結構
- PostgreSQL 原始碼解讀(109)- WAL#5(相關資料結構)SQL原始碼資料結構
- PostgreSQL 原始碼解讀(244)- plpgsql(CreateFunction-ProcedureCreate)SQL原始碼Function
- PostgreSQL 原始碼解讀(221)- Locks(PROCLOCK Struct)SQL原始碼Struct
- PostgreSQL 原始碼解讀(159)-PG Tools#6(What does pg_rewind do)SQL原始碼
- PostgreSQL 原始碼解讀(45)- 查詢語句#30(query_planner函式#6)SQL原始碼函式
- Vue 原始碼解讀(7)—— Hook EventVue原始碼Hook