參考:https://www.cnblogs.com/flying-tiger/p/6100794.html/
Portal的字面意是“入口”吐根。在PostgreSQL中,Portal用來表示一個(gè)正在執(zhí)行的或者是可執(zhí)行的查詢的執(zhí)行狀態(tài)熊户。
Portal記錄了與執(zhí)行相關(guān)的所有信息坑匠,例如查詢樹、計(jì)劃樹和執(zhí)行狀態(tài)雨让。確實(shí)可以將Portal看作執(zhí)行查詢計(jì)劃的“入口”雇盖。
對(duì)于用戶提交的普通查詢語句,在服務(wù)端會(huì)創(chuàng)建一個(gè)匿名的Portal對(duì)象栖忠。而對(duì)于SQL中的游標(biāo)聲明語句崔挖,也會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的Portal對(duì)象。
Portal的數(shù)據(jù)
執(zhí)行器使用PortalData
來存儲(chǔ)查詢計(jì)劃樹庵寞、結(jié)果元組的描述符(TupleDesc)狸相、執(zhí)行狀態(tài)、查詢結(jié)果等數(shù)據(jù)捐川。PortalData
結(jié)構(gòu)體定義在src/include/utils/portal.h
中脓鹃。
typedef struct PortalData
{
/* Bookkeeping data */
const char *name; /* portal's name */
......
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
const char *commandTag; /* command tag for original query */
List *stmts; /* PlannedStmts and/or utility statements */
......
ParamListInfo portalParams; /* params to pass to query */
/* Features/options */
PortalStrategy strategy; /* see above */
/* If not NULL, Executor is active; call ExecutorEnd eventually: */
QueryDesc *queryDesc; /* info needed for executor invocation */
/* If portal returns tuples, this is their tupdesc: */
TupleDesc tupDesc; /* descriptor for result tuples */
......
} PortalData;
Portal的狀態(tài)
Portal有六種狀態(tài),它們的定義如下:
/*
* A portal is always in one of these states. It is possible to transit
* from ACTIVE back to READY if the query is not run to completion;
* otherwise we never back up in status.
*/
typedef enum PortalStatus
{
PORTAL_NEW, /* freshly created */
PORTAL_DEFINED, /* PortalDefineQuery done */
PORTAL_READY, /* PortalStart complete, can run it */
PORTAL_ACTIVE, /* portal is running (can't delete it) */
PORTAL_DONE, /* portal is finished (don't re-run it) */
PORTAL_FAILED /* portal got error (can't re-run it) */
} PortalStatus;
在Portal剛被創(chuàng)建時(shí)古沥,狀態(tài)為PORTAL_NEW
瘸右。
通過PortalDefineQuery
將查詢計(jì)劃等數(shù)據(jù)設(shè)置到Portal上之后娇跟,Portal的狀態(tài)變?yōu)?code>PORTAL_DEFINED。
通過PortalStart
為執(zhí)行做好準(zhǔn)備之后太颤,Portal的狀態(tài)變?yōu)?code>PORTAL_READY逞频。
在PortalRun
的開始,會(huì)將Portal的狀態(tài)標(biāo)記為PORTAL_ACTIVE
栋齿。在PORTAL_MULTI_QUERY
的執(zhí)行策略下苗胀,在執(zhí)行完成后會(huì)將Portal的狀態(tài)變更為PORTAL_DONE
,而在其它執(zhí)行策略下瓦堵,會(huì)將狀態(tài)變更為PORTAL_READY
基协。
而在上述過程中發(fā)生錯(cuò)誤,都會(huì)將Portal的狀態(tài)設(shè)置為PORTAL_FAILED
菇用。
可優(yōu)化語句和不可優(yōu)化語句
PostgreSQL將用戶輸入的SQL語句分為兩類:可優(yōu)化語句(Optimizable Statement)和不可優(yōu)化語句(Non-optimizable Statement)澜驮。
可優(yōu)化語句就是通常講的DML語句,包括INSERT/DELETE/UPDATE/SELECT惋鸥。這類語句都要查詢到滿足條件的遠(yuǎn)組并返回給用戶杂穷。在查詢優(yōu)化階段會(huì)根據(jù)查詢優(yōu)化理論進(jìn)行重寫和優(yōu)化以提高查詢效率,因此稱為可優(yōu)化語句卦绣。
不可優(yōu)化語句包括DDL耐量、DCL等語句,例如創(chuàng)建表滤港、刪除表廊蜒、創(chuàng)建用戶等。這類語句包含查詢數(shù)據(jù)之外的各類操作溅漾,功能相對(duì)獨(dú)立山叮,因此也稱為功能性語句。功能性語句沒有優(yōu)化的價(jià)值添履。
Portal的執(zhí)行策略
Portal有五種執(zhí)行策略:
typedef enum PortalStrategy
{
PORTAL_ONE_SELECT,
PORTAL_ONE_RETURNING,
PORTAL_ONE_MOD_WITH,
PORTAL_UTIL_SELECT,
PORTAL_MULTI_QUERY
} PortalStrategy;
Portal采用哪種優(yōu)化策略取決于執(zhí)行的是什么樣子的查詢屁倔。需要注意的是,在用戶角度看到的一個(gè)單一的查詢語句經(jīng)過查詢重寫之后可能會(huì)變成零個(gè)或多個(gè)實(shí)際的查詢暮胧。
-
PORTAL_ONE_SELECT
Portal只包含一個(gè)
SELECT
查詢锐借。因?yàn)樾枰樵兘Y(jié)果,因此執(zhí)行器被遞增的執(zhí)行叔壤。這個(gè)策略也支持holdable的游標(biāo)(執(zhí)行結(jié)果可以存儲(chǔ)到tuplestore中瞎饲,以便在事務(wù)完成后訪問)口叙。 -
PORTAL_ONE_RETURNING
Portal包含一個(gè)帶有
RETURNING
子句(另外也可能有查詢重寫增加的輔助查詢)的INSERT/UPDATE/DELETE
查詢炼绘。在第一次執(zhí)行時(shí),Portal被執(zhí)行完成妄田,并將主查詢的結(jié)果存儲(chǔ)到Portal的tuplestore中俺亮,然后將結(jié)果返回給客戶端驮捍。 -
PORTAL_ONE_MOD_WITH
Portal包含一個(gè)
SELECT
查詢,同時(shí)存在修改數(shù)據(jù)的CTE(Common Table Expression)脚曾。當(dāng)前處理方式與PORTAL_ONE_RETURNING
相同东且。 -
PORTAL_UTIL_SELECT
Portal包含一個(gè)功能性(Utility)語句,返回類似于
SELECT
的結(jié)果(比如EXPLAIN和SHOW)本讥。在第一次執(zhí)行時(shí)珊泳,Portal被執(zhí)行完成,并將主查詢的結(jié)果存儲(chǔ)到Portal的tuplestore中拷沸,然后將結(jié)果返回給客戶端色查。 -
PORTAL_MULTI_QUERY
包含所有其它情況。Portal不支持部分執(zhí)行:在Portal第一次執(zhí)行時(shí)完成所有的查詢撞芍。
具體代碼位于src/backend/tcop/pquery.c
中函數(shù)ChoosePortalStrategy
秧了。
Portal的執(zhí)行
所有SQL語句的執(zhí)行都必須從一個(gè)Portal開始。Portal的執(zhí)行流程依次經(jīng)歷PortalStart
序无、PortalRun
验毡、PortalDrop
三個(gè)過程。每種執(zhí)行策略都實(shí)現(xiàn)了單獨(dú)的執(zhí)行流程帝嗡,會(huì)經(jīng)歷不同的處理過程晶通。
所有流程都在exec_simple_query
函數(shù)內(nèi)部進(jìn)行。Portal的執(zhí)行流程如下:
調(diào)用函數(shù)
CreatePortal
創(chuàng)建一個(gè)干凈的Portal哟玷,它的內(nèi)存上下文录择、資源跟蹤器清理函數(shù)都已經(jīng)設(shè)置好,但是sourceText碗降、stmts字段還未設(shè)置隘竭;調(diào)用函數(shù)
PortalDefineQuery
為剛剛創(chuàng)建的Portal設(shè)置sourceText、stmt等讼渊,并且設(shè)置Portal的狀態(tài)為PORTAL_DEFINED动看;-
調(diào)用函數(shù)
PortalStart
對(duì)定義好的Portal進(jìn)行初始化:調(diào)用函數(shù)
ChoosePortalStrategy
為portal選擇策略;如果選擇的是
PORTAL_ONE_SELECT
爪幻,則調(diào)用CreateQueryDesc
為Portal創(chuàng)建查詢描述符菱皆;如果選擇的是
PORTAL_ONE_RETURNING
或者PORTAL_ONE_MOD_WITH
,則調(diào)用ExecCleanTypeFromTL
為Portal創(chuàng)建返回元組的描述符挨稿;對(duì)于
PORTAL_UTIL_SELECT
則調(diào)用UtilityTupleDescriptor
為Portal創(chuàng)建查詢描述符仇轻;對(duì)于
PORTAL_MULTI_QUERY
這里則不做操作;將Portal的狀態(tài)設(shè)置為
PORTAL_READY
奶甘。
調(diào)用函數(shù)
PortalRun
執(zhí)行Portal,這就按照既定的策略調(diào)用相關(guān)執(zhí)行部件執(zhí)行Portal篷店;調(diào)用函數(shù)
PortalDrop
清理Portal,釋放資源。