本節(jié)介紹了PortalStart函數(shù),該函數(shù)在create_simple_query中被調(diào)用,用于執(zhí)行前初始化portal結(jié)構(gòu)體中的相關(guān)信息。
我們提供的服務(wù)有:成都網(wǎng)站制作、網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、鎮(zhèn)沅ssl等。為上千多家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的鎮(zhèn)沅網(wǎng)站制作公司
Portal
包括場(chǎng)景PortalStrategy枚舉定義/PortalStatus狀態(tài)定義/PortalData結(jié)構(gòu)體.Portal是PortalData結(jié)構(gòu)體指針,詳見代碼注釋.
/*
* We have several execution strategies for Portals, depending on what
* query or queries are to be executed. (Note: in all cases, a Portal
* executes just a single source-SQL query, and thus produces just a
* single result from the user's viewpoint. However, the rule rewriter
* may expand the single source query to zero or many actual queries.)
* 對(duì)于Portals(客戶端請(qǐng)求),有幾種執(zhí)行策略,具體取決于要執(zhí)行什么查詢。
* (注意:無(wú)論什么情況下,一個(gè)Portal只執(zhí)行一個(gè)source-SQL查詢,因此從用戶的角度來(lái)看只產(chǎn)生一個(gè)結(jié)果。
* 但是,規(guī)則重寫器可以將單個(gè)源查詢擴(kuò)展為零或多個(gè)實(shí)際查詢。
*
* PORTAL_ONE_SELECT: the portal contains one single SELECT query. We run
* the Executor incrementally as results are demanded. This strategy also
* supports holdable cursors (the Executor results can be dumped into a
* tuplestore for access after transaction completion).
* PORTAL_ONE_SELECT: 包含一個(gè)SELECT查詢。
* 按需要的結(jié)果重復(fù)(遞增)地運(yùn)行執(zhí)行器。
* 該策略還支持可持有游標(biāo)(執(zhí)行器結(jié)果可以在事務(wù)完成后轉(zhuǎn)儲(chǔ)到tuplestore中進(jìn)行訪問(wèn))。
*
* PORTAL_ONE_RETURNING: the portal contains a single INSERT/UPDATE/DELETE
* query with a RETURNING clause (plus possibly auxiliary queries added by
* rule rewriting). On first execution, we run the portal to completion
* and dump the primary query's results into the portal tuplestore; the
* results are then returned to the client as demanded. (We can't support
* suspension of the query partway through, because the AFTER TRIGGER code
* can't cope, and also because we don't want to risk failing to execute
* all the auxiliary queries.)
* PORTAL_ONE_RETURNING: 包含一個(gè)帶有RETURNING子句的INSERT/UPDATE/DELETE查詢
(可能還包括由規(guī)則重寫添加的輔助查詢)。
* 在第一次執(zhí)行時(shí),運(yùn)行Portal來(lái)完成并將主查詢的結(jié)果轉(zhuǎn)儲(chǔ)到Portal的tuplestore中;
* 然后根據(jù)需要將結(jié)果返回給客戶端。
* (我們不能支持半途中斷的查詢,因?yàn)锳FTER觸發(fā)器代碼無(wú)法處理,
* 也因?yàn)椴幌朊皥?zhí)行所有輔助查詢失敗的風(fēng)險(xiǎn))。
*
* PORTAL_ONE_MOD_WITH: the portal contains one single SELECT query, but
* it has data-modifying CTEs. This is currently treated the same as the
* PORTAL_ONE_RETURNING case because of the possibility of needing to fire
* triggers. It may act more like PORTAL_ONE_SELECT in future.
* PORTAL_ONE_MOD_WITH: 只包含一個(gè)SELECT查詢,但它具有數(shù)據(jù)修改的CTEs。
* 這與PORTAL_ONE_RETURNING的情況相同,因?yàn)榭赡苄枰|發(fā)觸發(fā)器。將來(lái)它的行為可能更像PORTAL_ONE_SELECT。
*
* PORTAL_UTIL_SELECT: the portal contains a utility statement that returns
* a SELECT-like result (for example, EXPLAIN or SHOW). On first execution,
* we run the statement and dump its results into the portal tuplestore;
* the results are then returned to the client as demanded.
* PORTAL_UTIL_SELECT: 包含一個(gè)實(shí)用程序語(yǔ)句,該語(yǔ)句返回一個(gè)類似SELECT的結(jié)果(例如,EXPLAIN或SHOW)。
* 在第一次執(zhí)行時(shí),運(yùn)行語(yǔ)句并將其結(jié)果轉(zhuǎn)儲(chǔ)到portal tuplestore;然后根據(jù)需要將結(jié)果返回給客戶端。
*
* PORTAL_MULTI_QUERY: all other cases. Here, we do not support partial
* execution: the portal's queries will be run to completion on first call.
* PORTAL_MULTI_QUERY: 除上述情況外的其他情況。
* 在這里,不支持部分執(zhí)行:Portal的查詢語(yǔ)句將在第一次調(diào)用時(shí)運(yùn)行到完成。
*/
typedef enum PortalStrategy
{
PORTAL_ONE_SELECT,
PORTAL_ONE_RETURNING,
PORTAL_ONE_MOD_WITH,
PORTAL_UTIL_SELECT,
PORTAL_MULTI_QUERY
} PortalStrategy;
/*
* 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.
* Portal總是處于這些狀態(tài)中的之一。
* 如果查詢沒(méi)有運(yùn)行到完成,則可以從活動(dòng)狀態(tài)轉(zhuǎn)回準(zhǔn)備狀態(tài);否則永遠(yuǎn)不會(huì)后退。
*/
typedef enum PortalStatus
{
PORTAL_NEW, /* 剛創(chuàng)建;freshly created */
PORTAL_DEFINED, /* PortalDefineQuery完成;PortalDefineQuery done */
PORTAL_READY, /* PortalStart完成;PortalStart complete, can run it */
PORTAL_ACTIVE, /* Portal正在運(yùn)行;portal is running (can't delete it) */
PORTAL_DONE, /* Portal已經(jīng)完成;portal is finished (don't re-run it) */
PORTAL_FAILED /* Portal出現(xiàn)錯(cuò)誤;portal got error (can't re-run it) */
} PortalStatus;
typedef struct PortalData *Portal;//結(jié)構(gòu)體指針
typedef struct PortalData
{
/* Bookkeeping data */
const char *name; /* portal的名稱;portal's name */
const char *prepStmtName; /* 已完成準(zhǔn)備的源語(yǔ)句;source prepared statement (NULL if none) */
MemoryContext portalContext; /* 內(nèi)存上下文;subsidiary memory for portal */
ResourceOwner resowner; /* 資源的owner;resources owned by portal */
void (*cleanup) (Portal portal); /* cleanup鉤子函數(shù);cleanup hook */
/*
* State data for remembering which subtransaction(s) the portal was
* created or used in. If the portal is held over from a previous
* transaction, both subxids are InvalidSubTransactionId. Otherwise,
* createSubid is the creating subxact and activeSubid is the last subxact
* in which we ran the portal.
* 狀態(tài)數(shù)據(jù),用于記住在哪個(gè)子事務(wù)中創(chuàng)建或使用Portal。
* 如果Portal是從以前的事務(wù)中持有的,那么兩個(gè)subxids都應(yīng)該是InvalidSubTransactionId。
* 否則,createSubid是正在創(chuàng)建的subxact,而activeSubid是運(yùn)行Portal的最后一個(gè)subxact。
*/
SubTransactionId createSubid; /* 正在創(chuàng)建的subxact;the creating subxact */
SubTransactionId activeSubid; /* 活動(dòng)的最后一個(gè)subxact;the last subxact with activity */
/* The query or queries the portal will execute */
//portal將會(huì)執(zhí)行的查詢
const char *sourceText; /* 查詢的源文本;text of query (as of 8.4, never NULL) */
const char *commandTag; /* 源查詢的命令tag;command tag for original query */
List *stmts; /* PlannedStmt鏈表;list of PlannedStmts */
CachedPlan *cplan; /* 緩存的PlannedStmts;CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* 傳遞給查詢的參數(shù);params to pass to query */
QueryEnvironment *queryEnv; /* 查詢的執(zhí)行環(huán)境;environment for query */
/* Features/options */
PortalStrategy strategy; /* 場(chǎng)景;see above */
int cursorOptions; /* DECLARE CURSOR選項(xiàng)位;DECLARE CURSOR option bits */
bool run_once; /* 是否只執(zhí)行一次;portal will only be run once */
/* Status data */
PortalStatus status; /* Portal的狀態(tài);see above */
bool portalPinned; /* 是否不能被清除;a pinned portal can't be dropped */
bool autoHeld; /* 是否自動(dòng)從pinned到held;was automatically converted from pinned to
* held (see HoldPinnedPortals()) */
/* If not NULL, Executor is active; call ExecutorEnd eventually: */
//如不為NULL,執(zhí)行器處于活動(dòng)狀態(tài)
QueryDesc *queryDesc; /* 執(zhí)行器需要使用的信息;info needed for executor invocation */
/* If portal returns tuples, this is their tupdesc: */
//如Portal需要返回元組,這是元組的描述
TupleDesc tupDesc; /* 結(jié)果元組的描述;descriptor for result tuples */
/* and these are the format codes to use for the columns: */
//列信息的格式碼
int16 *formats; /* 每一列的格式碼;a format code for each column */
/*
* Where we store tuples for a held cursor or a PORTAL_ONE_RETURNING or
* PORTAL_UTIL_SELECT query. (A cursor held past the end of its
* transaction no longer has any active executor state.)
* 在這里,為持有的游標(biāo)或PORTAL_ONE_RETURNING或PORTAL_UTIL_SELECT存儲(chǔ)元組。
* (在事務(wù)結(jié)束后持有的游標(biāo)不再具有任何活動(dòng)執(zhí)行器狀態(tài)。)
*/
Tuplestorestate *holdStore; /* 存儲(chǔ)持有的游標(biāo)信息;store for holdable cursors */
MemoryContext holdContext; /* 持有holdStore的內(nèi)存上下文;memory containing holdStore */
/*
* Snapshot under which tuples in the holdStore were read. We must keep a
* reference to this snapshot if there is any possibility that the tuples
* contain TOAST references, because releasing the snapshot could allow
* recently-dead rows to be vacuumed away, along with any toast data
* belonging to them. In the case of a held cursor, we avoid needing to
* keep such a snapshot by forcibly detoasting the data.
* 讀取holdStore中元組的Snapshot。
* 如果元組包含TOAST引用的可能性存在,那么必須保持對(duì)該快照的引用,
* 因?yàn)獒尫趴煺湛赡軙?huì)使最近廢棄的行與屬于它們的TOAST數(shù)據(jù)一起被清除。
* 對(duì)于持有的游標(biāo),通過(guò)強(qiáng)制解壓數(shù)據(jù)來(lái)避免需要保留這樣的快照。
*/
Snapshot holdSnapshot; /* 已注冊(cè)的快照信息,如無(wú)則為NULL;registered snapshot, or NULL if none */
/*
* atStart, atEnd and portalPos indicate the current cursor position.
* portalPos is zero before the first row, N after fetching N'th row of
* query. After we run off the end, portalPos = # of rows in query, and
* atEnd is true. Note that atStart implies portalPos == 0, but not the
* reverse: we might have backed up only as far as the first row, not to
* the start. Also note that various code inspects atStart and atEnd, but
* only the portal movement routines should touch portalPos.
* atStart、atEnd和portalPos表示當(dāng)前光標(biāo)的位置。
* portalPos在第一行之前為0,在獲取第N行查詢后為N。
* 在運(yùn)行結(jié)束后,portalPos = #查詢中的行號(hào),atEnd為T。
* 注意,atStart表示portalPos == 0,但不是相反:我們可能只回到到第一行,而不是開始。
* 還要注意,各種代碼在開始和結(jié)束時(shí)都要檢查,但是只有Portal移動(dòng)例程應(yīng)該訪問(wèn)portalPos。
*/
bool atStart;//處于開始位置?
bool atEnd;//處于結(jié)束位置?
uint64 portalPos;//實(shí)際行號(hào)
/* Presentation data, primarily used by the pg_cursors system view */
//用于表示的數(shù)據(jù),主要由pg_cursors系統(tǒng)視圖使用
TimestampTz creation_time; /* portal定義的時(shí)間;time at which this portal was defined */
bool visible; /* 是否在pg_cursors中可見? include this portal in pg_cursors? */
} PortalData;
/*
* PortalIsValid
* True iff portal is valid.
* 判斷Portal是否有效
*/
#define PortalIsValid(p) PointerIsValid(p)
QueryDesc
QueryDesc封裝了執(zhí)行器執(zhí)行查詢所需的所有內(nèi)容。
/* ----------------
* query descriptor:
*
* a QueryDesc encapsulates everything that the executor
* needs to execute the query.
* QueryDesc封裝了執(zhí)行器執(zhí)行查詢所需的所有內(nèi)容。
*
* For the convenience of SQL-language functions, we also support QueryDescs
* containing utility statements; these must not be passed to the executor
* however.
* 為了使用SQL函數(shù),還需要支持包含實(shí)用語(yǔ)句的QueryDescs;
* 但是,這些內(nèi)容不能傳遞給執(zhí)行程序。
* ---------------------
*/
typedef struct QueryDesc
{
/* These fields are provided by CreateQueryDesc */
//以下變量由CreateQueryDesc函數(shù)設(shè)置
CmdType operation; /* 操作類型,如CMD_SELECT等;CMD_SELECT, CMD_UPDATE, etc. */
PlannedStmt *plannedstmt; /* 已規(guī)劃的語(yǔ)句,規(guī)劃器的輸出;planner's output (could be utility, too) */
const char *sourceText; /* 源SQL文本;source text of the query */
Snapshot snapshot; /* 查詢使用的快照;snapshot to use for query */
Snapshot crosscheck_snapshot; /* RI 更新/刪除交叉檢查快照;crosscheck for RI update/delete */
DestReceiver *dest; /* 元組輸出的接收器;the destination for tuple output */
ParamListInfo params; /* 需傳入的參數(shù)值;param values being passed in */
QueryEnvironment *queryEnv; /* 查詢環(huán)境變量;query environment passed in */
int instrument_options; /* InstrumentOption選項(xiàng);OR of InstrumentOption flags */
/* These fields are set by ExecutorStart */
//以下變量由ExecutorStart函數(shù)設(shè)置
TupleDesc tupDesc; /* 結(jié)果元組tuples描述;descriptor for result tuples */
EState *estate; /* 執(zhí)行器狀態(tài);executor's query-wide state */
PlanState *planstate; /* per-plan-node狀態(tài)樹;tree of per-plan-node state */
/* This field is set by ExecutorRun */
//以下變量由ExecutorRun設(shè)置
bool already_executed; /* 先前已執(zhí)行,則為T;true if previously executed */
/* This is always set NULL by the core system, but plugins can change it */
//內(nèi)核設(shè)置為NULL,可由插件修改
struct Instrumentation *totaltime; /* ExecutorRun函數(shù)所花費(fèi)的時(shí)間;total time spent in ExecutorRun */
} QueryDesc;
PortalStart
PortalStart函數(shù)的作用是在執(zhí)行SQL語(yǔ)句前初始化portal結(jié)構(gòu)體中的相關(guān)信息,其中有2個(gè)重要數(shù)據(jù)結(jié)構(gòu)的初始化:
1.調(diào)用CreateQueryDesc函數(shù)結(jié)構(gòu)體QueryDesc
2.調(diào)用ExecutorStart函數(shù)初始化結(jié)構(gòu)體EState,ExecutorStart函數(shù)調(diào)用InitPlan(下一節(jié)介紹)初始化計(jì)劃狀態(tài)樹.
/*
* PortalStart
* Prepare a portal for execution.
* 執(zhí)行前初始化portal結(jié)構(gòu)體中的相關(guān)信息
*
* Caller must already have created the portal, done PortalDefineQuery(),
* and adjusted portal options if needed.
* 調(diào)用者必須已經(jīng)創(chuàng)建了portal,完成PortalDefineQuery()函數(shù)的調(diào)用,并且已經(jīng)調(diào)整了portal中的相關(guān)選項(xiàng)
*
* If parameters are needed by the query, they must be passed in "params"
* (caller is responsible for giving them appropriate lifetime).
* 如果查詢需要提供參數(shù),通過(guò)"params"參數(shù)傳入
* (調(diào)用者負(fù)責(zé)參數(shù)的生命周期管理)
*
* The caller can also provide an initial set of "eflags" to be passed to
* ExecutorStart (but note these can be modified internally, and they are
* currently only honored for PORTAL_ONE_SELECT portals). Most callers
* should simply pass zero.
* 調(diào)用者需要提供"eflags"變量的初始化集合,該參數(shù)用于傳遞給函數(shù)ExecutorStart
* (要注意eflags可以在內(nèi)部修改,它們目前只在PORTAL_ONE_SELECT中才會(huì)被使用)
* 大多數(shù)的調(diào)用者只應(yīng)該傳遞參數(shù)0
*
* The caller can optionally pass a snapshot to be used; pass InvalidSnapshot
* for the normal behavior of setting a new snapshot. This parameter is
* presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended
* to be used for cursors).
* 調(diào)用者可以選擇傳遞要使用的快照;為設(shè)置新快照的正常行為傳遞InvalidSnapshot。
* 這個(gè)參數(shù)目前僅用于PORTAL_ONE_SELECT使用(用于游標(biāo))。
*
* On return, portal is ready to accept PortalRun() calls, and the result
* tupdesc (if any) is known.
* 該函數(shù)返回時(shí),portal已做好接收PortalRun()調(diào)用返回的準(zhǔn)備,結(jié)果tupdesc是已知的.
*/
void
PortalStart(Portal portal, ParamListInfo params,
int eflags, Snapshot snapshot)
{
Portal saveActivePortal;
ResourceOwner saveResourceOwner;
MemoryContext savePortalContext;
MemoryContext oldContext;
QueryDesc *queryDesc;
int myeflags;
AssertArg(PortalIsValid(portal));
AssertState(portal->status == PORTAL_DEFINED);
/*
* Set up global portal context pointers.
* 設(shè)置全局portal上下文指針
*/
//保護(hù)"現(xiàn)場(chǎng)"
saveActivePortal = ActivePortal;
saveResourceOwner = CurrentResourceOwner;
savePortalContext = PortalContext;
PG_TRY();
{
ActivePortal = portal;
if (portal->resowner)
CurrentResourceOwner = portal->resowner;
PortalContext = portal->portalContext;
oldContext = MemoryContextSwitchTo(PortalContext);
/* Must remember portal param list, if any */
//記錄傳遞的參數(shù)信息
portal->portalParams = params;
/*
* Determine the portal execution strategy
* 確定portal執(zhí)行場(chǎng)景
*/
portal->strategy = ChoosePortalStrategy(portal->stmts);
/*
* Fire her up according to the strategy
* 根據(jù)場(chǎng)景觸發(fā)相應(yīng)的處理
*/
switch (portal->strategy)
{
case PORTAL_ONE_SELECT://PORTAL_ONE_SELECT
/* Must set snapshot before starting executor. */
//在開始執(zhí)行前必須設(shè)置快照snapshot
if (snapshot)
PushActiveSnapshot(snapshot);
else
PushActiveSnapshot(GetTransactionSnapshot());
/*
* Create QueryDesc in portal's context; for the moment, set
* the destination to DestNone.
* 在portal上下文中創(chuàng)建QueryDesc,同時(shí)設(shè)置接收的目標(biāo)為DestNone
*/
queryDesc = CreateQueryDesc(linitial_node(PlannedStmt, portal->stmts),
portal->sourceText,
GetActiveSnapshot(),
InvalidSnapshot,
None_Receiver,
params,
portal->queryEnv,
0);
/*
* If it's a scrollable cursor, executor needs to support
* REWIND and backwards scan, as well as whatever the caller
* might've asked for.
* 游標(biāo)可滾動(dòng),執(zhí)行器需要支持REWIND和向后的掃描
*/
if (portal->cursorOptions & CURSOR_OPT_SCROLL)
myeflags = eflags | EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;
else
myeflags = eflags;
/*
* Call ExecutorStart to prepare the plan for execution
* 調(diào)用ExecutorStart,為執(zhí)行做準(zhǔn)備
*/
ExecutorStart(queryDesc, myeflags);
/*
* This tells PortalCleanup to shut down the executor
* 告知PortalCleanup關(guān)閉執(zhí)行器
*/
portal->queryDesc = queryDesc;
/*
* Remember tuple descriptor (computed by ExecutorStart)
* 記錄tuple描述符(queryDesc->tupDesc)
*/
portal->tupDesc = queryDesc->tupDesc;
/*
* Reset cursor position data to "start of query"
* 重置游標(biāo)位置數(shù)據(jù)為"開始查詢"
*/
portal->atStart = true;//開始的位置
portal->atEnd = false; /* 允許可獲取數(shù)據(jù);allow fetches */
portal->portalPos = 0;//游標(biāo)位置
PopActiveSnapshot();
break;
case PORTAL_ONE_RETURNING:
case PORTAL_ONE_MOD_WITH:
/*
* We don't start the executor until we are told to run the
* portal. We do need to set up the result tupdesc.
* 執(zhí)行器在調(diào)用的時(shí)候才會(huì)啟動(dòng),需要配置結(jié)果tupdesc.
*
*/
{
PlannedStmt *pstmt;
pstmt = PortalGetPrimaryStmt(portal);//獲取主stmt
portal->tupDesc =
ExecCleanTypeFromTL(pstmt->planTree->targetlist,
false);//設(shè)置元組描述符
}
/*
* Reset cursor position data to "start of query"
* 重置游標(biāo)位置
*/
portal->atStart = true;
portal->atEnd = false; /* allow fetches */
portal->portalPos = 0;
break;
case PORTAL_UTIL_SELECT://PORTAL_UTIL_SELECT
/*
* We don't set snapshot here, because PortalRunUtility will
* take care of it if needed.
*/
{
PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
Assert(pstmt->commandType == CMD_UTILITY);
portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
}
/*
* Reset cursor position data to "start of query"
*/
portal->atStart = true;
portal->atEnd = false; /* allow fetches */
portal->portalPos = 0;
break;
case PORTAL_MULTI_QUERY://PORTAL_MULTI_QUERY
/* Need do nothing now */
portal->tupDesc = NULL;
break;
}
}
PG_CATCH();
{
/* Uncaught error while executing portal: mark it dead */
MarkPortalFailed(portal);
/* Restore global vars and propagate error */
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldContext);
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
portal->status = PORTAL_READY;
}
/*
* ChoosePortalStrategy
* Select portal execution strategy given the intended statement list.
* 根據(jù)預(yù)期的語(yǔ)句鏈表選擇portal執(zhí)行策略。
*
* The list elements can be Querys or PlannedStmts.
* That's more general than portals need, but plancache.c uses this too.
* 鏈表中的元素可以是Query或者是PlannedStmt.
* 這比portal需要的更普遍,plancache.c也用這個(gè)。
*
*
* See the comments in portal.h.
* 參見portal.h中的注釋.
*/
PortalStrategy
ChoosePortalStrategy(List *stmts)
{
int nSetTag;
ListCell *lc;
/*
* PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
* single-statement case, since there are no rewrite rules that can add
* auxiliary queries to a SELECT or a utility command. PORTAL_ONE_MOD_WITH
* likewise allows only one top-level statement.
* PORTAL_ONE_SELECT和PORTAL_UTIL_SELECT只需要考慮單語(yǔ)句的情況,
* 因?yàn)闆](méi)有可以向SELECT或?qū)嵱贸绦蛎钐砑虞o助查詢的重寫規(guī)則。
* PORTAL_ONE_MOD_WITH同樣只允許一個(gè)最上層語(yǔ)句。
*/
if (list_length(stmts) == 1)//只有1條語(yǔ)句
{
Node *stmt = (Node *) linitial(stmts);//獲取stmt
if (IsA(stmt, Query))//Query
{
Query *query = (Query *) stmt;
if (query->canSetTag)
{
if (query->commandType == CMD_SELECT)//查詢命令
{
if (query->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH;//存在可更新的CTE-->PORTAL_ONE_MOD_WITH
else
return PORTAL_ONE_SELECT;//單個(gè)查詢語(yǔ)句
}
if (query->commandType == CMD_UTILITY)//工具語(yǔ)句
{
if (UtilityReturnsTuples(query->utilityStmt))//返回元組
return PORTAL_UTIL_SELECT;//PORTAL_UTIL_SELECT
/* it can't be ONE_RETURNING, so give up */
return PORTAL_MULTI_QUERY;//返回PORTAL_MULTI_QUERY
}
}
}
else if (IsA(stmt, PlannedStmt))//PlannedStmt,參見Query處理邏輯
{
PlannedStmt *pstmt = (PlannedStmt *) stmt;
if (pstmt->canSetTag)
{
if (pstmt->commandType == CMD_SELECT)
{
if (pstmt->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH;
else
return PORTAL_ONE_SELECT;
}
if (pstmt->commandType == CMD_UTILITY)
{
if (UtilityReturnsTuples(pstmt->utilityStmt))
return PORTAL_UTIL_SELECT;
/* it can't be ONE_RETURNING, so give up */
return PORTAL_MULTI_QUERY;
}
}
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
}
//存在多條語(yǔ)句
/*
* PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
* Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and
* it has a RETURNING list.
* PORTAL_ONE_RETURNING必須允許通過(guò)重寫添加輔助查詢。
* 如果只有一個(gè)canSetTag查詢,并且它有一個(gè)RETURNING鏈表,那么選擇PORTAL_ONE_RETURNING。
*/
nSetTag = 0;
foreach(lc, stmts)//遍歷
{
Node *stmt = (Node *) lfirst(lc);
if (IsA(stmt, Query))
{
Query *query = (Query *) stmt;
if (query->canSetTag)
{
if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */
if (query->commandType == CMD_UTILITY ||
query->returningList == NIL)
return PORTAL_MULTI_QUERY; /* no need to look further */
}
}
else if (IsA(stmt, PlannedStmt))
{
PlannedStmt *pstmt = (PlannedStmt *) stmt;
if (pstmt->canSetTag)
{
if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */
if (pstmt->commandType == CMD_UTILITY ||
!pstmt->hasReturning)
return PORTAL_MULTI_QUERY; /* no need to look further */
}
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
}
if (nSetTag == 1)
return PORTAL_ONE_RETURNING;
/* Else, it's the general case... */
//通常的情況
return PORTAL_MULTI_QUERY;
}
/*
* CreateQueryDesc
* 構(gòu)造QueryDesc結(jié)構(gòu)體
*/
QueryDesc *
CreateQueryDesc(PlannedStmt *plannedstmt,
const char *sourceText,
Snapshot snapshot,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
QueryEnvironment *queryEnv,
int instrument_options)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
qd->operation = plannedstmt->commandType; /* 操作類型;operation */
qd->plannedstmt = plannedstmt; /* 已規(guī)劃的SQL語(yǔ)句;plan */
qd->sourceText = sourceText; /* 源SQL文本;query text */
qd->snapshot = RegisterSnapshot(snapshot); /* 快照;snapshot */
/* RI check snapshot */
qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
qd->dest = dest; /* 輸出的目標(biāo)端;output dest */
qd->params = params; /* 傳入到查詢語(yǔ)句中的參數(shù)值;parameter values passed into query */
qd->queryEnv = queryEnv; //查詢環(huán)境變量
qd->instrument_options = instrument_options; /* 是否需要instrumentation;instrumentation wanted? */
/* null these fields until set by ExecutorStart */
qd->tupDesc = NULL;//初始化為NULL
qd->estate = NULL;
qd->planstate = NULL;
qd->totaltime = NULL;
/* not yet executed */
qd->already_executed = false;//未執(zhí)行
return qd;
}
/* ----------------------------------------------------------------
* ExecutorStart
*
* This routine must be called at the beginning of any execution of any
* query plan
* ExecutorStart必須在執(zhí)行開始前調(diào)用.
*
* Takes a QueryDesc previously created by CreateQueryDesc (which is separate
* only because some places use QueryDescs for utility commands). The tupDesc
* field of the QueryDesc is filled in to describe the tuples that will be
* returned, and the internal fields (estate and planstate) are set up.
* 獲取先前由CreateQueryDesc創(chuàng)建的QueryDesc(該數(shù)據(jù)結(jié)構(gòu)是獨(dú)立的,只是因?yàn)橛行┑胤绞褂肣ueryDesc來(lái)執(zhí)行實(shí)用命令)。
* 填充QueryDesc的tupDesc字段,以描述將要返回的元組,并設(shè)置內(nèi)部字段(estate和planstate)。
*
* eflags contains flag bits as described in executor.h.
* eflags存儲(chǔ)標(biāo)志位(在executor.h中有說(shuō)明)
*
* NB: the CurrentMemoryContext when this is called will become the parent
* of the per-query context used for this Executor invocation.
* 注意:CurrentMemoryContext會(huì)成為每個(gè)執(zhí)行查詢的上下文的parent
*
* We provide a function hook variable that lets loadable plugins
* get control when ExecutorStart is called. Such a plugin would
* normally call standard_ExecutorStart().
* 我們提供了一個(gè)函數(shù)鉤子變量,可以讓可加載插件在調(diào)用ExecutorStart時(shí)獲得控制權(quán)。
* 這樣的插件通常會(huì)調(diào)用standard_ExecutorStart()函數(shù)。
*
* ----------------------------------------------------------------
*/
void
ExecutorStart(QueryDesc *queryDesc, int eflags)
{
if (ExecutorStart_hook)//存在鉤子函數(shù)
(*ExecutorStart_hook) (queryDesc, eflags);
else
standard_ExecutorStart(queryDesc, eflags);
}
void
standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
EState *estate;
MemoryContext oldcontext;
/* sanity checks: queryDesc must not be started already */
Assert(queryDesc != NULL);
Assert(queryDesc->estate == NULL);
/*
* If the transaction is read-only, we need to check if any writes are
* planned to non-temporary tables. EXPLAIN is considered read-only.
* 如果事務(wù)是只讀的,需要檢查是否計(jì)劃對(duì)非臨時(shí)表進(jìn)行寫操作。
* EXPLAIN命令被認(rèn)為是只讀的。
*
* Don't allow writes in parallel mode. Supporting UPDATE and DELETE
* would require (a) storing the combocid hash in shared memory, rather
* than synchronizing it just once at the start of parallelism, and (b) an
* alternative to heap_update()'s reliance on xmax for mutual exclusion.
* INSERT may have no such troubles, but we forbid it to simplify the
* checks.
* 不要在并行模式下寫。
* 支持更新和刪除需要:
* (a)在共享內(nèi)存中存儲(chǔ)combocid散列,而不是在并行性開始時(shí)只同步一次;
* (b) heap_update()依賴xmax實(shí)現(xiàn)互斥的替代方法。
* INSERT可能沒(méi)有這樣的麻煩,但我們禁止它簡(jiǎn)化檢查。
*
* We have lower-level defenses in CommandCounterIncrement and elsewhere
* against performing unsafe operations in parallel mode, but this gives a
* more user-friendly error message.
* 在CommandCounterIncrement和其他地方,對(duì)于在并行模式下執(zhí)行不安全的操作,
* PG有較低級(jí)別的防御,這里提供了更用戶友好的錯(cuò)誤消息。
*/
if ((XactReadOnly || IsInParallelMode()) &&
!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
ExecCheckXactReadOnly(queryDesc->plannedstmt);
/*
* Build EState, switch into per-query memory context for startup.
* 構(gòu)建EState,切換至每個(gè)查詢的上下文中,準(zhǔn)備開啟執(zhí)行
*/
estate = CreateExecutorState();
queryDesc->estate = estate;
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
/*
* Fill in external parameters, if any, from queryDesc; and allocate
* workspace for internal parameters
* 填充queryDesc的外部參數(shù)(如有);并為內(nèi)部參數(shù)分配工作區(qū)
*/
estate->es_param_list_info = queryDesc->params;
if (queryDesc->plannedstmt->paramExecTypes != NIL)
{
int nParamExec;
nParamExec = list_length(queryDesc->plannedstmt->paramExecTypes);
estate->es_param_exec_vals = (ParamExecData *)
palloc0(nParamExec * sizeof(ParamExecData));
}
estate->es_sourceText = queryDesc->sourceText;
/*
* Fill in the query environment, if any, from queryDesc.
* 填充查詢執(zhí)行環(huán)境,從queryDesc中獲得
*/
estate->es_queryEnv = queryDesc->queryEnv;
/*
* If non-read-only query, set the command ID to mark output tuples with
* 非只讀查詢,設(shè)置命令I(lǐng)D
*/
switch (queryDesc->operation)
{
case CMD_SELECT:
/*
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark
* tuples
* SELECT FOR [KEY] UPDATE/SHARE和正在更新的CTEs需要標(biāo)記元組
*/
if (queryDesc->plannedstmt->rowMarks != NIL ||
queryDesc->plannedstmt->hasModifyingCTE)
estate->es_output_cid = GetCurrentCommandId(true);
/*
* A SELECT without modifying CTEs can't possibly queue triggers,
* so force skip-triggers mode. This is just a marginal efficiency
* hack, since AfterTriggerBeginQuery/AfterTriggerEndQuery aren't
* all that expensive, but we might as well do it.
* 不帶更新CTEs的SELECT不可能執(zhí)行觸發(fā)器,因此強(qiáng)制為EXEC_FLAG_SKIP_TRIGGERS標(biāo)記.
* 這只是一個(gè)邊際效益問(wèn)題,因?yàn)锳fterTriggerBeginQuery/AfterTriggerEndQuery成本并不高,但不妨這樣做。
*/
if (!queryDesc->plannedstmt->hasModifyingCTE)
eflags |= EXEC_FLAG_SKIP_TRIGGERS;
break;
case CMD_INSERT:
case CMD_DELETE:
case CMD_UPDATE:
estate->es_output_cid = GetCurrentCommandId(true);
break;
default:
elog(ERROR, "unrecognized operation code: %d",
(int) queryDesc->operation);
break;
}
/*
* Copy other important information into the EState
* 拷貝其他重要的信息到EState數(shù)據(jù)結(jié)構(gòu)中
*/
estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot);
estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot);
estate->es_top_eflags = eflags;
estate->es_instrument = queryDesc->instrument_options;
estate->es_jit_flags = queryDesc->plannedstmt->jitFlags;
/*
* Set up an AFTER-trigger statement context, unless told not to, or
* unless it's EXPLAIN-only mode (when ExecutorFinish won't be called).
* 設(shè)置AFTER-trigger語(yǔ)句上下文,除非明確不需要執(zhí)行此操作或者是EXPLAIN-only模式
*/
if (!(eflags & (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY)))
AfterTriggerBeginQuery();
/*
* Initialize the plan state tree
* 初始化計(jì)劃狀態(tài)樹
*/
InitPlan(queryDesc, eflags);
MemoryContextSwitchTo(oldcontext);
}
測(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)入PortalStart函數(shù)
(gdb) b PortalStart
Breakpoint 1 at 0x8cb67b: file pquery.c, line 455.
(gdb) c
Continuing.
Breakpoint 1, PortalStart (portal=0x25cd468, params=0x0, eflags=0, snapshot=0x0) at pquery.c:455
455 AssertArg(PortalIsValid(portal));
校驗(yàn)并保護(hù)現(xiàn)場(chǎng)
455 AssertArg(PortalIsValid(portal));
(gdb) n
456 AssertState(portal->status == PORTAL_DEFINED);
(gdb)
461 saveActivePortal = ActivePortal;
(gdb)
462 saveResourceOwner = CurrentResourceOwner;
(gdb)
463 savePortalContext = PortalContext;
設(shè)置內(nèi)存上下文,資源owner等信息
466 ActivePortal = portal;
(gdb)
467 if (portal->resowner)
(gdb)
468 CurrentResourceOwner = portal->resowner;
(gdb)
469 PortalContext = portal->portalContext;
(gdb)
471 oldContext = MemoryContextSwitchTo(PortalContext);
(gdb)
474 portal->portalParams = params;
場(chǎng)景為PORTAL_ONE_SELECT
(gdb) p portal->strategy
$1 = PORTAL_ONE_SELECT
根據(jù)strategy,進(jìn)入相應(yīng)的處理分支(PORTAL_ONE_SELECT)
設(shè)置快照
489 if (snapshot)
(gdb)
492 PushActiveSnapshot(GetTransactionSnapshot());
創(chuàng)建QueryDesc結(jié)構(gòu)體
(gdb)
498 queryDesc = CreateQueryDesc(linitial_node(PlannedStmt, portal->stmts),
查看queryDesc結(jié)構(gòu)體信息
(gdb) n
512 if (portal->cursorOptions & CURSOR_OPT_SCROLL)
(gdb) p *queryDesc
$2 = {operation = CMD_SELECT, plannedstmt = 0x2650df0,
sourceText = 0x2567eb8 "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>...,
snapshot = 0x260ce10, crosscheck_snapshot = 0x0, dest = 0xf8f280 <donothingDR>, params = 0x0, queryEnv = 0x0,
instrument_options = 0, tupDesc = 0x0, estate = 0x0, planstate = 0x0, already_executed = false, totaltime = 0x0}
設(shè)置標(biāo)記位
(gdb) n
515 myeflags = eflags;
(gdb) p eflags
$3 = 0
進(jìn)入ExecutorStart函數(shù)
(gdb) n
147 standard_ExecutorStart(queryDesc, eflags);
(gdb) step
standard_ExecutorStart (queryDesc=0x2657f68, eflags=0) at execMain.c:157
157 Assert(queryDesc != NULL);
ExecutorStart-->執(zhí)行相關(guān)校驗(yàn)和判斷
157 Assert(queryDesc != NULL);
(gdb) n
158 Assert(queryDesc->estate == NULL);
(gdb)
175 if ((XactReadOnly || IsInParallelMode()) &&
ExecutorStart-->創(chuàng)建EState,初始化EState結(jié)構(gòu)體
(gdb)
182 estate = CreateExecutorState();
(gdb)
183 queryDesc->estate = estate;
(gdb) p *estate
$4 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x0, es_crosscheck_snapshot = 0x0,
es_range_table = 0x0, es_plannedstmt = 0x0, es_sourceText = 0x0, 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 = 0x2653e30, es_tupleTable = 0x0, es_rowMarks = 0x0,
es_processed = 0, es_lastoid = 0, es_top_eflags = 0, es_instrument = 0, es_finished = false, es_exprcontexts = 0x0,
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}
ExecutorStart-->EState結(jié)構(gòu)體中的變量賦值
(gdb) n
185 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
(gdb)
191 estate->es_param_list_info = queryDesc->params;
(gdb)
193 if (queryDesc->plannedstmt->paramExecTypes != NIL)
(gdb)
202 estate->es_sourceText = queryDesc->sourceText;
(gdb)
207 estate->es_queryEnv = queryDesc->queryEnv;
ExecutorStart-->根據(jù)queryDesc->operation的不同執(zhí)行的處理
(gdb)
212 switch (queryDesc->operation)
(gdb)
220 if (queryDesc->plannedstmt->rowMarks != NIL ||
(gdb) p queryDesc->operation
$5 = CMD_SELECT
(gdb) n
221 queryDesc->plannedstmt->hasModifyingCTE)
(gdb)
220 if (queryDesc->plannedstmt->rowMarks != NIL ||
(gdb)
230 if (!queryDesc->plannedstmt->hasModifyingCTE)
(gdb)
231 eflags |= EXEC_FLAG_SKIP_TRIGGERS;
(gdb)
232 break;
ExecutorStart-->設(shè)置快照
(gdb) n
249 estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot);
(gdb) p *queryDesc->snapshot
$6 = {satisfies = 0xa923ca <HeapTupleSatisfiesMVCC>, xmin = 1689, xmax = 1689, xip = 0x0, xcnt = 0, subxip = 0x0,
subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = true, curcid = 0, speculativeToken = 0,
active_count = 1, regd_count = 1, ph_node = {first_child = 0x0, next_sibling = 0x0, prev_or_parent = 0x0}, whenTaken = 0,
lsn = 0}
ExecutorStart-->設(shè)置其他EState中的變量
(gdb) n
250 estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot);
(gdb) p *queryDesc->crosscheck_snapshot
Cannot access memory at address 0x0
(gdb) n
251 estate->es_top_eflags = eflags;
(gdb)
252 estate->es_instrument = queryDesc->instrument_options;
(gdb)
253 estate->es_jit_flags = queryDesc->plannedstmt->jitFlags;
(gdb)
259 if (!(eflags & (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY)))
ExecutorStart-->執(zhí)行InitPlan
(gdb)
265 InitPlan(queryDesc, eflags);
(gdb)
267 MemoryContextSwitchTo(oldcontext);
(gdb)
268 }
ExecutorStart-->查看QueryDesc和EState
(gdb) p *queryDesc
$7 = {operation = CMD_SELECT, plannedstmt = 0x2650df0,
sourceText = 0x2567eb8 "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>...,
snapshot = 0x25e46c0, crosscheck_snapshot = 0x0, dest = 0xf8f280 <donothingDR>, params = 0x0, queryEnv = 0x0,
instrument_options = 0, tupDesc = 0x2665058, estate = 0x2653f48, planstate = 0x2654160, already_executed = false,
totaltime = 0x0}
(gdb) p *estate
$8 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x25e46c0, es_crosscheck_snapshot = 0x0,
es_range_table = 0x264ec98, es_plannedstmt = 0x2650df0,
es_sourceText = 0x2567eb8 "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 = 0x2653e30, es_tupleTable = 0x2654af8, es_rowMarks = 0x0, es_processed = 0,
es_lastoid = 0, es_top_eflags = 16, es_instrument = 0, es_finished = false, es_exprcontexts = 0x2654550,
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}
ExecutorStart-->回到PortalStart
(gdb) n
ExecutorStart (queryDesc=0x2657f68, eflags=0) at execMain.c:148
148 }
(gdb) n
PortalStart (portal=0x25cd468, params=0x0, eflags=0, snapshot=0x0) at pquery.c:525
525 portal->queryDesc = queryDesc;
設(shè)置portal中的變量atStart等
525 portal->queryDesc = queryDesc;
(gdb) n
530 portal->tupDesc = queryDesc->tupDesc;
(gdb)
535 portal->atStart = true;
(gdb)
536 portal->atEnd = false; /* allow fetches */
(gdb)
537 portal->portalPos = 0;
(gdb)
539 PopActiveSnapshot();
(gdb)
540 break;
執(zhí)行完畢,回到exec_simple_query
(gdb)
613 portal->status = PORTAL_READY;
(gdb)
614 }
(gdb)
exec_simple_query (
query_string=0x2567eb8 "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>...) at postgres.c:1091
warning: Source file is more recent than executable.
1091 format = 0; /* TEXT is default */
(gdb)
DONE!
postgres.c
PG Document:Query Planning
網(wǎng)站名稱:PostgreSQL源碼解讀(83)-查詢語(yǔ)句#68(PortalStart函數(shù))
本文地址:http://www.rwnh.cn/article22/peoijc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、動(dòng)態(tài)網(wǎng)站、建站公司、Google、服務(wù)器托管、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)