91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PostgreSQL中Review exec_simple_query函數的實現邏輯是什么

發布時間:2021-11-10 15:49:23 來源:億速云 閱讀:117 作者:iii 欄目:關系型數據庫

這篇文章主要介紹“PostgreSQL中Review exec_simple_query函數的實現邏輯是什么”,在日常操作中,相信很多人在PostgreSQL中Review exec_simple_query函數的實現邏輯是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中Review exec_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;
}

到此,關于“PostgreSQL中Review exec_simple_query函數的實現邏輯是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

万源市| 准格尔旗| 静乐县| 独山县| 同仁县| 锡林郭勒盟| 新泰市| 宝清县| 宜黄县| 石台县| 广平县| 峨眉山市| 肥城市| 仁怀市| 赣榆县| 浮梁县| 喀喇沁旗| 永靖县| 宁强县| 鲜城| 洪泽县| 阳东县| 甘洛县| 澎湖县| 顺义区| 恭城| 卫辉市| 内乡县| 沙坪坝区| 洛南县| 奉节县| 双桥区| 竹山县| 朝阳县| 平罗县| 开阳县| 武陟县| 孙吴县| 合阳县| 新化县| 卢龙县|