數(shù)據(jù)庫(kù)內(nèi)核分析之GPDB and PostgreSQL Portal
GPDB and PostgreSQL Portal內(nèi)核分析
0.導(dǎo)論
Portal(門戶),也稱為策略選擇模塊,根據(jù)sql語(yǔ)句類型選擇不同的執(zhí)行模塊(ProcessUtility、Executor)。
SQL語(yǔ)句類型包括:可優(yōu)化語(yǔ)句、數(shù)據(jù)定義語(yǔ)句。
可優(yōu)化語(yǔ)句包括DML,像insert/update/select等語(yǔ)句,這類語(yǔ)句特點(diǎn)是查詢滿足條件的元組返回給用戶或者元組操作后寫入磁盤,之所以稱之為可優(yōu)化語(yǔ)句是因?yàn)檫@類語(yǔ)句通常會(huì)被優(yōu)化器進(jìn)行重寫與優(yōu)化,從而加快查詢速度。
數(shù)據(jù)定義語(yǔ)句主要是功能性語(yǔ)句,例如:DDL(Create、Alter、Drop)、DCL(Grant、COMMIT、ROLLBACK)等。

1.Portal
1.1 入口層
QD執(zhí)行會(huì)從exec_simple_query進(jìn)入,QE執(zhí)行從exec_mpp_query進(jìn)入。
1.2 Portal層
1.2.1 初識(shí)Portal
首先初識(shí)Portal內(nèi)部數(shù)據(jù)結(jié)構(gòu):

策略只包含一個(gè)SELECT查詢。
包含一個(gè)INSERT/UPDATE/DELETE查詢,且?guī)ETURNING條件。
包含一個(gè)SELECT查詢并且有修改的CTE。
例如:下面這個(gè)就不是PORTAL_ONE_MOD_WITH,而是PORTAL_MULTI_QUERY。
包含一個(gè)utility語(yǔ)句,且該語(yǔ)句執(zhí)行會(huì)返回像SELECT那樣有輸出結(jié)果。
其他情況,例如:PORTAL_MULTI_QUERY + PORTAL_MULTI_QUERY
- PORTAL_MULTI_QUERY
- PORTAL_UTIL_SELECT
- PORTAL_ONE_MOD_WITH
- PORTAL_ONE_RETURNING
- PORTAL_ONE_SELECT
狀態(tài)
- PORTAL_NEW
- PORTAL_DEFINED
- PORTAL_READY
- PORTAL_QUEUE
- PORTAL_ACTIVE
- PORTAL_DONE
- PORTAL_FAILED
這幾個(gè)狀態(tài)會(huì)在下面依次引入。
1.2.2 CreatePortal
創(chuàng)建一個(gè)新的Portal,傳入?yún)?shù)均為: CreatePortal("", true, true),表示創(chuàng)建一個(gè)匿名的Portal,允許重復(fù)且重復(fù)后保持沉默。
CreatePortal邏輯:
- 根據(jù)傳入的第一個(gè)參數(shù)name從哈希表中查找
- 根據(jù)傳入的第二個(gè)參數(shù)allowDup,如果第一步查找到,從哈希表中決定是否刪除。如果true,則刪除,否則報(bào)錯(cuò)。在哈希表中查找到Portal且允許重復(fù)的情況下,在QD節(jié)點(diǎn)上會(huì)根據(jù)第三個(gè)參數(shù)dupSilent決定是否輸出告警信息。
- 創(chuàng)建一個(gè)新的Portal,并初始化相應(yīng)參數(shù)。
執(zhí)行完畢后,便創(chuàng)建好了一個(gè)狀態(tài)為PORTAL_NEW的Portal。
1.2.3 PortalDefineQuery
定義portal數(shù)據(jù),包含了:查詢語(yǔ)句sourceText、PlannedStmts、查詢完成標(biāo)記qc。
注意:QD上根據(jù)傳遞進(jìn)來(lái)的stmt來(lái)設(shè)置nodeTag,但是QE上為T_Query,因?yàn)镼E上不是parsed statement,所以不是 T_SelectStmt。
最終設(shè)置Portal狀態(tài)為PORTAL_DEFINED。
1.2.4 PortalStart
準(zhǔn)備好portal,主要有如下幾步:
- 設(shè)置ddesc,該信息為QD到QE上的額外信息,QD上為NULL,QE上不為NULL。
- 設(shè)置全局參數(shù),例如:當(dāng)前活躍的portal、resourceOwner、context。
- 設(shè)置portal參數(shù)字段:portalParams,同樣QD上為NULL,QE上不為NULL。
- 設(shè)置portal策略(ChoosePortalStrategy)。
如下圖所示:輸入為查詢計(jì)劃鏈表,針對(duì)PORTAL_ONE_SELECT、PORTAL_ONE_MOD_WITH、PORTAL_UTIL_SELECT、PORTAL_ONE_RETURNING都是要求一個(gè)計(jì)劃,首先判斷是Query還是PlannedStmt,一般情況下的查詢語(yǔ)句基本都是PlannedStmt,對(duì)于像PREPARE st(int) as select * from t1之類utility語(yǔ)句第一次調(diào)用ChoosePortalStrategy返回PORTAL_MULTI_QUERY(命中PlannedStmt),第二次調(diào)用返回PORTAL_ONE_SELECT(命中Query)。

choose
5. 根據(jù)portal策略初始化portal,最重要的是初始化tupDesc與cursor postion。

例如:"QUERY PLAN"、"t1_id、col1"就是tupDesc。
不同tupleDesc函數(shù)區(qū)別
- ExecTypeFromTL
skip resjunk column
- ExecCleanTypeFromTL
with resjunk column
6. 在執(zhí)行Portal過(guò)程中發(fā)生異常,設(shè)置portal的狀態(tài)為PORTAL_FAILED;否則,下一步。
7. 設(shè)置Portal狀態(tài)為PORTAL_READY。
1.2.5 PortalRun
根據(jù)sql的語(yǔ)句類型選擇不同的執(zhí)行路徑,獲取元組數(shù)據(jù),完成portal工作,運(yùn)行完之后要么Done要么下一輪(READY,而非ACTIVE)。
portal策略執(zhí)行路徑如下:
- PORTAL_ONE_SELECT
set result = portal->atEnd
- PORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT
獲取時(shí)數(shù)據(jù)方向包含前進(jìn)/后退
可以從holdStore中獲取,也可以從ExectorRun中獲取
填充holdStore(見(jiàn)下方)
調(diào)用PortalRunSelect返回n行數(shù)據(jù)
設(shè)置狀態(tài)為PORTAL_READY
設(shè)置是否完成運(yùn)行標(biāo)記為portal->atEnd
- PORTAL_MULTI_QUERY
調(diào)用PortalRunMulti
設(shè)置狀態(tài)為PORTAL_Done
設(shè)置是否完成運(yùn)行標(biāo)記為true

此外,上述圖中填充holdStore邏輯如下:
- 調(diào)用PortalCreateHoldStore填充portal->holdStore,通過(guò)工廠函數(shù)CreateDestReceiver構(gòu)造DestReceiver(子類:TStoreState);
- 根據(jù)portal策略執(zhí)行查詢:PORTAL_ONE_RETURNING、PORTAL_ONE_MOD_WITH 調(diào)用PortalRunMulti,PORTAL_UTIL_SELECT調(diào)用PortalRunUtility。
PortalRunUtility
PortalRunMulti
ProcessQuery
PortalRunUtility
utilityStmt
not utilityStmt
PORTAL_ONE_RETURNING、PORTAL_ONE_MOD_WITH
PORTAL_UTIL_SELECT

2.游標(biāo)Cursor
2.1 打開(kāi)游標(biāo)
如果不想一次執(zhí)行整個(gè)命令,可以設(shè)置一個(gè)封裝該命令的游標(biāo)(cursor), 然后每次讀取幾行命令結(jié)果。
例如:
該命令運(yùn)行機(jī)制為:首先識(shí)別到是一個(gè)數(shù)據(jù)定義語(yǔ)句,便會(huì)調(diào)用ProcessUtility,隨后解析從PlannedStmt中的utilityStmt識(shí)別出是一個(gè)T_DeclareCursorStmt節(jié)點(diǎn),調(diào)用PerformCursorOpen執(zhí)行Declare cursor命令。
PerformCursorOpen處理邏輯如下:
- query重寫
- 優(yōu)化器優(yōu)化,生成PlannedStmt
- 創(chuàng)建Portal(名字為游標(biāo)名),僅調(diào)用PortalStart
2.2 關(guān)閉游標(biāo)
關(guān)閉游標(biāo),實(shí)際就是關(guān)閉Portal,調(diào)用PerformPortalClose。
如下兩個(gè)操作:
如果傳入的名字為空,則是CLOSE ALL關(guān)閉所有非活躍portal,否則,只關(guān)閉指定的portal(cursor)。
2.3 FETCH or MOVE
FETCH與MOVE語(yǔ)法分別如下:
FETCH從游標(biāo)中檢索n行到目標(biāo)中, 目標(biāo)可以是一個(gè)行變量、記錄變量、逗號(hào)分隔的普通變量列表, 就像SELECT INTO一樣, 如果沒(méi)有獲取到數(shù)據(jù),目標(biāo)會(huì)設(shè)為NULL。
MOVE重新定位一個(gè)游標(biāo),而不需要檢索任何數(shù)據(jù),例如:一旦游標(biāo)位置確定,則可以刪除或更新行。
從實(shí)現(xiàn)層面兩者都會(huì)進(jìn)入到PerformPortalFetch,都被解析為FetchStmt,內(nèi)部有個(gè)成員ismove決定是MOVE還是FETCH。
不管是哪個(gè),都會(huì)指定cursor名,有了這個(gè)名字,便知道了portal,隨后調(diào)用PortalRunFetch來(lái)獲取結(jié)果。
PortalRunFetch內(nèi)部會(huì)像PortalRun運(yùn)行一樣,首先設(shè)置portal狀態(tài)為Active,隨后根據(jù)策略選擇不同的調(diào)用鏈。
- PORTAL_ONE_SELECT
調(diào)用DoPortalRunFetch
- PORTAL_ONE_RETURNING|PORTAL_ONE_MOD_WITH|PORTAL_UTIL_SELECT
首先判斷portal內(nèi)部是否有holdStore,如果沒(méi)有會(huì)調(diào)用FillPortalStore,隨后調(diào)用DoPortalRunFetch。
DoPortalRunFetch內(nèi)部實(shí)現(xiàn),會(huì)考慮傳入的direction,決定是前向還是后向等不同方向的掃描,最后調(diào)用PortalRunSelect獲取數(shù)據(jù),注意:gpdb不支持backward scan,但是pg支持。


























