PostgreSQL 原始碼解讀(81)- 查詢語句#66(Review - exec_simp...
本節Review exec_simple_query函式的實現邏輯,該函式響應並處理"Simple Query"協議的訊息請求。
一、原始碼解讀
exec_simple_query函式由PostgresMain函式呼叫,其呼叫棧如下:
#3 0x00000000008c5c35 in exec_simple_query (query_string=0x2eed1a8 "select * from t1;") at postgres.c:1050
#4 0x00000000008ca0f8 in PostgresMain (argc=1, argv=0x2f16cb8, dbname=0x2f16b20 "testdb", username=0x2ee9d48 "xdb")
at postgres.c:4159
#5 0x0000000000825880 in BackendRun (port=0x2f0eb00) at postmaster.c:4361
#6 0x0000000000824fe4 in BackendStartup (port=0x2f0eb00) at postmaster.c:4033
#7 0x0000000000821371 in ServerLoop () at postmaster.c:1706
#8 0x0000000000820c09 in PostmasterMain (argc=1, argv=0x2ee7d00) at postmaster.c:1379
#9 0x0000000000747ea7 in main (argc=1, argv=0x2ee7d00) at main.c:228
該函式的實現邏輯詳見程式碼註釋.
/*
* exec_simple_query
*
* Execute a "simple Query" protocol message.
* 響應並執行"simple Query"協議訊息請求
*/
/*
輸入:
query_string-SQL語句
輸出:
無
*/
static void
exec_simple_query(const char *query_string)
{
CommandDest dest = whereToSendOutput;//輸出到哪裡的定義
MemoryContext oldcontext;//儲存原記憶體上下文
List *parsetree_list;//分析樹列表
ListCell *parsetree_item;//分析樹中的ITEM
bool save_log_statement_stats = log_statement_stats;//是否儲存統計資訊,false
bool was_logged = false;//Log?
bool use_implicit_block;//是否使用隱式事務塊
char msec_str[32];
/*
* Report query to various monitoring facilities.
* 監控資訊
*/
debug_query_string = query_string;
pgstat_report_activity(STATE_RUNNING, query_string);//統計資訊
TRACE_POSTGRESQL_QUERY_START(query_string);
/*
* We use save_log_statement_stats so ShowUsage doesn't report incorrect
* results because ResetUsage wasn't called.
* 如save_log_statement_stats為T,則呼叫ResetUsage函式
*/
if (save_log_statement_stats)
ResetUsage();
/*
* Start up a transaction command. All queries generated by the
* query_string will be in this same command block, *unless* we find a
* BEGIN/COMMIT/ABORT statement; we have to force a new xact command after
* one of those, else bad things will happen in xact.c. (Note that this
* will normally change current memory context.)
* 啟動一個事務命令。
* 由query_string生成的所有查詢都將位於同一個命令塊中,*除非*找到BEGIN/COMMIT/ABORT語句;
* 必須在其中一個命令之後強制執行新的xact命令,否則xact.c中會發生意想不到事情。
* (注意,這通常會改變當前記憶體上下文。)
*/
start_xact_command();//啟動事務
/*
* Zap any pre-existing unnamed statement. (While not strictly necessary,
* it seems best to define simple-Query mode as if it used the unnamed
* statement and portal; this ensures we recover any storage used by prior
* unnamed operations.)
* 刪除任何預先存在的未命名語句。
* (雖然不是嚴格必要的,但最好定義簡單查詢模式,就像使用未命名語句和門戶介面一樣;
* 這確保我們恢復以前未命名操作所使用的儲存資訊。)
*/
drop_unnamed_stmt();//清除未命名語句
/*
* Switch to appropriate context for constructing parsetrees.
* 切換至合適的記憶體上下文
*/
oldcontext = MemoryContextSwitchTo(MessageContext);//切換記憶體上下文
/*
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
* 執行查詢(或多個查詢)的基本解析
* (即使我們處於中止的事務狀態,這也應該是安全的!)
*/
parsetree_list = pg_parse_query(query_string);//解析輸入的查詢語句,獲得解析樹List(元素是RawStmt nodes)
/* Log immediately if dictated by log_statement */
//如需要記錄日誌,則Log這些資訊
if (check_log_statement(parsetree_list))//日誌記錄
{
ereport(LOG,
(errmsg("statement: %s", query_string),
errhidestmt(true),
errdetail_execute(parsetree_list)));
was_logged = true;
}
/*
* Switch back to transaction context to enter the loop.
* 切換回去原事務上下文
*/
MemoryContextSwitchTo(oldcontext);//切換回原記憶體上下文
/*
* For historical reasons, if multiple SQL statements are given in a
* single "simple Query" message, we execute them as a single transaction,
* unless explicit transaction control commands are included to make
* portions of the list be separate transactions. To represent this
* behavior properly in the transaction machinery, we use an "implicit"
* transaction block.
* 由於歷史原因,如果在單個“簡單查詢”訊息中給出了多個SQL語句,那麼我們將作為單個事務執行它們,
* 除非包含顯式事務控制命令,使連結串列中的部分成為單獨的事務。
* 為了在事務機制中正確地表示這種行為,使用了一個“隱式”事務塊。
*/
//如果分析樹條目>1,使用隱式事務塊(多條SQL語句在同一個事務中)
use_implicit_block = (list_length(parsetree_list) > 1);
/*
* Run through the raw parsetree(s) and process each one.
* 對分析樹中的每一個條目進行處理
*/
foreach(parsetree_item, parsetree_list)//
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);//分析樹List中的元素為RawStmt指標型別
bool snapshot_set = false;//是否設定快照?
const char *commandTag;//命令標識
char completionTag[COMPLETION_TAG_BUFSIZE];//完成標記,如INSERT 0 1之類的字串
List *querytree_list,//查詢樹List
*plantree_list;//執行計劃List
Portal portal;//“門戶”變數
DestReceiver *receiver;//目標接收端
int16 format;//
/*
* Get the command name for use in status display (it also becomes the
* default completion tag, down inside PortalRun). Set ps_status and
* do any special start-of-SQL-command processing needed by the
* destination.
* 獲取用於狀態顯示的命令名稱(在PortalRun內部,它也成為預設的完成標記)。
* 設定ps_status並執行目標語句所需要的start-of-SQL-command處理。
*/
commandTag = CreateCommandTag(parsetree->stmt);//建立命令標記,插入資料則為INSERT
set_ps_display(commandTag, false);
BeginCommand(commandTag, dest);//do Nothing!
/*
* If we are in an aborted transaction, reject all commands except
* COMMIT/ABORT. It is important that this test occur before we try
* to do parse analysis, rewrite, or planning, since all those phases
* try to do database accesses, which may fail in abort state. (It
* might be safe to allow some additional utility commands in this
* state, but not many...)
* 如果我們處於事務中止狀態中,拒絕除COMMIT/ABORT之外的所有命令。
* 在嘗試進行解析分析、重寫或計劃之前進行此測試是很重要的,
* 因為所有這些階段都嘗試進行資料庫訪問,而在abort狀態下可能會失敗。
* (在這種狀態下允許一些額外的實用程式命令可能是安全的,但不是很多……)
*/
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(parsetree->stmt))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block"),
errdetail_abort()));
/* Make sure we are in a transaction command */
start_xact_command();//確認在事務中
/*
* If using an implicit transaction block, and we're not already in a
* transaction block, start an implicit block to force this statement
* to be grouped together with any following ones. (We must do this
* each time through the loop; otherwise, a COMMIT/ROLLBACK in the
* list would cause later statements to not be grouped.)
* 如果使用隱式事務塊(還沒有使用atransaction塊),啟動一個隱式事務塊來強制將該語句與以下語句組合在一起。
* (我們每次透過迴圈時都必須這樣做;否則,連結串列中的提交/回滾將導致後面的語句沒有分組。
*/
if (use_implicit_block)
BeginImplicitTransactionBlock();//隱式事務,進入事務塊
/* If we got a cancel signal in parsing or prior command, quit */
//如果在解析或者解析之前接收到取消的訊號,則退出
CHECK_FOR_INTERRUPTS();
/*
* Set up a snapshot if parse analysis/planning will need one.
* 如果解析/分析/計劃過程需要snapshot,則建立一個
*/
if (analyze_requires_snapshot(parsetree))//是否需要快照進行分析?增刪改查均需要
{
PushActiveSnapshot(GetTransactionSnapshot());//活動快照入棧
snapshot_set = true;
}
/*
* OK to analyze, rewrite, and plan this query.
* 準備工作妥當,可以進行分析/重寫和計劃查詢語句了
*
* Switch to appropriate context for constructing querytrees (again,
* these must outlive the execution context).
* 切換至合適的記憶體上下文中
*/
oldcontext = MemoryContextSwitchTo(MessageContext);//切換記憶體上下文
//根據分析樹獲得查詢樹,返回List(元素為Query)
querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
NULL, 0, NULL);
//根據查詢樹獲取計劃樹,返回List(元素為PlannedStmt)
plantree_list = pg_plan_queries(querytree_list,
CURSOR_OPT_PARALLEL_OK, NULL);
/* Done with the snapshot used for parsing/planning */
//啟用了快照,則出棧
if (snapshot_set)
PopActiveSnapshot();//
/* If we got a cancel signal in analysis or planning, quit */
//如接收到取消訊號,則退出
CHECK_FOR_INTERRUPTS();
/*
* Create unnamed portal to run the query or queries in. If there
* already is one, silently drop it.
* 建立匿名的門戶介面來執行查詢,如Portal已存在,則先drop it
*/
portal = CreatePortal("", true, true);//建立匿名Portal變數
/* Don't display the portal in pg_cursors */
//該portal不在pg_cursors中出現
portal->visible = false;
/*
* We don't have to copy anything into the portal, because everything
* we are passing here is in MessageContext, which will outlive the
* portal anyway.
* 不需要將任何內容複製到Portal中,
* 因為在這裡傳遞的所有內容都在MessageContext中,它的生命週期將比Portal更長。
*/
PortalDefineQuery(portal,
NULL,
query_string,
commandTag,
plantree_list,
NULL);//給Portal變數賦值
/*
* Start the portal. No parameters here.
* 啟動Portal,不需要引數
*/
PortalStart(portal, NULL, 0, InvalidSnapshot);//為PortalRun作準備
/*
* Select the appropriate output format: text unless we are doing a
* FETCH from a binary cursor. (Pretty grotty to have to do this here
* --- but it avoids grottiness in other places. Ah, the joys of
* backward compatibility...)
* 選擇適當的輸出格式:文字,除了我們正在從二進位制遊標進行提取。
* (不得不在這裡這樣做實在是太糟糕了——但在其他地方卻避免了這種情況。啊,向後相容的樂趣……
*/
format = 0; /* 預設為TEXT;TEXT is default */
if (IsA(parsetree->stmt, FetchStmt))
{
FetchStmt *stmt = (FetchStmt *) parsetree->stmt;
if (!stmt->ismove)
{
Portal fportal = GetPortalByName(stmt->portalname);
if (PortalIsValid(fportal) &&
(fportal->cursorOptions & CURSOR_OPT_BINARY))
format = 1; /* 二進位制格式;BINARY */
}
}
PortalSetResultFormat(portal, 1, &format);//設定結果返回的格式,預設為TEXT
/*
* Now we can create the destination receiver object.
* 現在可以建立目標接收器物件了
*/
//建立目標接收器(如使用psql則為:printtup DestReceiver)
receiver = CreateDestReceiver(dest);
if (dest == DestRemote)
SetRemoteDestReceiverParams(receiver, portal);
/*
* Switch back to transaction context for execution.
* 切換回原記憶體上下文
*/
MemoryContextSwitchTo(oldcontext);//
/*
* Run the portal to completion, and then drop it (and the receiver).
* 執行PortalRun,然後清除
*/
(void) PortalRun(portal,
FETCH_ALL,
true, /* always top level */
true,
receiver,
receiver,
completionTag);//執行
//執行完畢,銷燬接收器
receiver->rDestroy(receiver);
//清除Portal中的資源
PortalDrop(portal, false);
if (lnext(parsetree_item) == NULL)//所有語句已執行完畢
{
/*
* If this is the last parsetree of the query string, close down
* transaction statement before reporting command-complete. This
* is so that any end-of-transaction errors are reported before
* the command-complete message is issued, to avoid confusing
* clients who will expect either a command-complete message or an
* error, not one and then the other. Also, if we're using an
* implicit transaction block, we must close that out first.
* 如果這是查詢字串的最後一個parsetree,在報告命令完成之前關閉事務語句。
* 這樣,在發出命令完整的訊息之前就會報告任何事務結束錯誤,以避免混淆視聽,
* 因為客戶端希望看到命令完整的訊息或錯誤,而不是一個錯誤,然後是另一個錯誤。
* 另外,如果我們使用隱式事務塊,我們必須首先關閉它。
*/
if (use_implicit_block)
EndImplicitTransactionBlock();//結束事務
finish_xact_command();//結束事務
}
else if (IsA(parsetree->stmt, TransactionStmt))//事務語句?BEGIN/COMMIT/ABORT...
{
/*
* If this was a transaction control statement, commit it. We will
* start a new xact command for the next command.
* 如果這是一個事務控制語句,提交此語句。
* 我們將為下一個命令啟動一個新的xact命令。
*/
finish_xact_command();
}
else
{
/*
* We need a CommandCounterIncrement after every query, except
* those that start or end a transaction block.
* 在每次查詢之後,我們都需要一個CommandCounterIncrement,除了那些啟動或結束事務塊的查詢。
*/
CommandCounterIncrement();//命令+1(對應Tuple中的cid)
}
/*
* Tell client that we're done with this query. Note we emit exactly
* one EndCommand report for each raw parsetree, thus one for each SQL
* command the client sent, regardless of rewriting. (But a command
* aborted by error will not send an EndCommand report at all.)
* 告訴客戶端已經完成了這個查詢。注意,對於每個原始的parsetree,只發出一個EndCommand報告,
* 因此,對於客戶機傳送的每個SQL命令,只發出一個EndCommand報告,而不考慮重寫。
* (注:由於錯誤而中止的命令根本不會傳送EndCommand報告。)
*/
EndCommand(completionTag, dest);//命令Done
} /* end loop over parsetrees */
//所有語句結束
/*
* Close down transaction statement, if one is open. (This will only do
* something if the parsetree list was empty; otherwise the last loop
* iteration already did it.)
* 如果事務是開啟的,則關閉。
* (只有當parsetree連結串列為空時,才會執行某些操作;否則,最後一次迴圈迭代已經完成了。
*/
finish_xact_command();
/*
* If there were no parsetrees, return EmptyQueryResponse message.
* 如果parsetrees連結串列已清空,返回EmptyQueryResponse訊息
*/
if (!parsetree_list)
NullCommand(dest);
/*
* Emit duration logging if appropriate.
* 如需要記錄日誌,則在合適的時候記錄
*/
switch (check_log_duration(msec_str, was_logged))
{
case 1:
ereport(LOG,
(errmsg("duration: %s ms", msec_str),
errhidestmt(true)));
break;
case 2:
ereport(LOG,
(errmsg("duration: %s ms statement: %s",
msec_str, query_string),
errhidestmt(true),
errdetail_execute(parsetree_list)));
break;
}
if (save_log_statement_stats)
ShowUsage("QUERY STATISTICS");
TRACE_POSTGRESQL_QUERY_DONE(query_string);
debug_query_string = NULL;
}
二、參考資料
PG Document:Query Planning
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374809/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PostgreSQL 原始碼解讀(75)- 查詢語句#60(Review - standard_...SQL原始碼View
- PostgreSQL 原始碼解讀(74)- 查詢語句#59(Review - subquery_...SQL原始碼View
- PostgreSQL 原始碼解讀(50)- 查詢語句#35(Optimizer Review#1)SQL原始碼View
- PostgreSQL 原始碼解讀(51)- 查詢語句#36(Optimizer Review#2)SQL原始碼View
- PostgreSQL 原始碼解讀(66)- 查詢語句#51(make_one_rel函式#16-...SQL原始碼函式
- PostgreSQL 原始碼解讀(24)- 查詢語句#9(查詢重寫)SQL原始碼
- PostgreSQL 原始碼解讀(20)- 查詢語句#5(查詢樹Query詳解)SQL原始碼
- PostgreSQL 原始碼解讀(18)- 查詢語句#3(SQL Parse)SQL原始碼
- PostgreSQL 原始碼解讀(19)- 查詢語句#4(ParseTree詳解)SQL原始碼
- PostgreSQL 原始碼解讀(17)- 查詢語句#2(查詢優化基礎)SQL原始碼優化
- PostgreSQL 原始碼解讀(25)- 查詢語句#10(查詢優化概覽)SQL原始碼優化
- PostgreSQL 原始碼解讀(83)- 查詢語句#68(PortalStart函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(42)- 查詢語句#27(等價類)SQL原始碼
- PostgreSQL 原始碼解讀(29)- 查詢語句#14(查詢優化-上拉子查詢)SQL原始碼優化
- PostgreSQL 原始碼解讀(37)- 查詢語句#22(查詢優化-grouping_plan...SQL原始碼優化
- PostgreSQL 原始碼解讀(82)- 查詢語句#67(PortalXXX系列函式)SQL原始碼函式
- PostgreSQL 原始碼解讀(89)- 查詢語句#74(SeqNext函式#2)SQL原始碼函式
- PostgreSQL 原始碼解讀(90)- 查詢語句#75(ExecHashJoin函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(91)- 查詢語句#76(ExecHashJoin函式#2)SQL原始碼函式
- PostgreSQL 原始碼解讀(88)- 查詢語句#73(SeqNext函式#1)SQL原始碼函式
- PostgreSQL 原始碼解讀(87)- 查詢語句#72(PortalRunSelect->E...SQL原始碼
- PostgreSQL 原始碼解讀(84)- 查詢語句#69(PortalStart->InitP...SQL原始碼
- PostgreSQL 原始碼解讀(85)- 查詢語句#70(PortalRun->InitPla...SQL原始碼
- PostgreSQL 原始碼解讀(86)- 查詢語句#71(PortalRun->PortalR...SQL原始碼
- PostgreSQL 原始碼解讀(93)- 查詢語句#77(ExecHashJoin函式#3)SQL原始碼函式
- PostgreSQL 原始碼解讀(36)- 查詢語句#21(查詢優化-消除外連線)SQL原始碼優化
- PostgreSQL 原始碼解讀(21)- 查詢語句#6(PlannedStmt詳解-跟蹤分析)SQL原始碼
- PostgreSQL 原始碼解讀(73)- 查詢語句#58(grouping_planner函式...SQL原始碼函式
- PostgreSQL 原始碼解讀(23)- 查詢語句#8(PlannedStmt與QUERY P...SQL原始碼
- PostgreSQL 原始碼解讀(187)- 查詢#103(聚合函式#8 - Struct Review)SQL原始碼函式StructView
- PostgreSQL 原始碼解讀(188)- 查詢#104(聚合函式#8 - ExecAgg Review)SQL原始碼函式View
- PostgreSQL 原始碼解讀(95)- 查詢語句#78(ExecHashJoin函式#4-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(97)- 查詢語句#79(ExecHashJoin函式#5-H...SQL原始碼函式
- PostgreSQL 原始碼解讀(16)- 查詢語句#1(基礎:關係代數)SQL原始碼
- PostgreSQL 原始碼解讀(43)- 查詢語句#28(query_planner函式#5)SQL原始碼函式
- PostgreSQL 原始碼解讀(45)- 查詢語句#30(query_planner函式#6)SQL原始碼函式
- PostgreSQL 原始碼解讀(46)- 查詢語句#31(query_planner函式#7)SQL原始碼函式
- PostgreSQL 原始碼解讀(47)- 查詢語句#32(query_planner函式#8)SQL原始碼函式