支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本: devel
不受支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

32.4. 非同步命令處理 #

PQexec 函式足以在正常的同步應用程式中提交命令。然而,它有一些缺點,對某些使用者來說可能很重要

  • PQexec 等待命令完成。應用程式可能還有其他工作要做(例如維護使用者介面),在這種情況下,它不希望被阻塞等待回應。

  • 由於客戶端應用程式的執行會在等待結果時暫停,因此應用程式很難決定它是否想要嘗試取消正在進行的命令。(它可以從訊號處理常式中完成,但不能以其他方式完成。)

  • PQexec 只能傳回一個 PGresult 結構。如果提交的命令字串包含多個SQL命令,則除了最後一個 PGresult 之外,所有其他的都會被 PQexec 丟棄。

  • PQexec 始終收集命令的整個結果,並將其緩衝在單個 PGresult 中。雖然這簡化了應用程式的錯誤處理邏輯,但對於包含許多列的結果來說,這可能是不切實際的。

不喜歡這些限制的應用程式可以改用 PQexec 所依賴的底層函式: PQsendQueryPQgetResult。還有 PQsendQueryParamsPQsendPreparePQsendQueryPreparedPQsendDescribePreparedPQsendDescribePortalPQsendClosePreparedPQsendClosePortal,它們可以與 PQgetResult 一起使用,以複製 PQexecParamsPQpreparePQexecPreparedPQdescribePreparedPQdescribePortal PQclosePreparedPQclosePortal 的功能。

PQsendQuery #

將命令提交到伺服器,而不等待結果。如果命令成功發送則傳回 1,否則傳回 0(在這種情況下,請使用 PQerrorMessage 來取得有關失敗的更多資訊)。

int PQsendQuery(PGconn *conn, const char *command);

成功呼叫 PQsendQuery 後,呼叫 PQgetResult 一次或多次以取得結果。在 PQgetResult 傳回空指標(表示命令已完成)之前,不能(在同一個連線上)再次呼叫 PQsendQuery

在管線模式下,不允許使用此函式。

PQsendQueryParams #

將命令和個別參數提交到伺服器,而不等待結果。

int PQsendQueryParams(PGconn *conn,
                      const char *command,
                      int nParams,
                      const Oid *paramTypes,
                      const char * const *paramValues,
                      const int *paramLengths,
                      const int *paramFormats,
                      int resultFormat);

這等同於 PQsendQuery,只是查詢參數可以與查詢字串分開指定。該函式的參數的處理方式與 PQexecParams 相同。與 PQexecParams 類似,它只允許在查詢字串中使用一個命令。

PQsendPrepare #

發送請求以使用給定參數建立預先準備的語句,而無需等待完成。

int PQsendPrepare(PGconn *conn,
                  const char *stmtName,
                  const char *query,
                  int nParams,
                  const Oid *paramTypes);

這是 PQprepare 的非同步版本:如果它能夠發送請求,則返回 1,否則返回 0。成功呼叫後,請呼叫 PQgetResult 以確定伺服器是否成功建立預先處理的陳述式。此函式的參數處理方式與 PQprepare 相同。

PQsendQueryPrepared #

發送請求以執行帶有給定參數的預先處理陳述式,而不等待結果。

int PQsendQueryPrepared(PGconn *conn,
                        const char *stmtName,
                        int nParams,
                        const char * const *paramValues,
                        const int *paramLengths,
                        const int *paramFormats,
                        int resultFormat);

這與 PQsendQueryParams 相似,但要執行的命令是透過命名先前準備好的陳述式來指定的,而不是給定查詢字串。此函式的參數處理方式與 PQexecPrepared 相同。

PQsendDescribePrepared #

提交請求以取得有關指定預先處理陳述式的資訊,而不等待完成。

int PQsendDescribePrepared(PGconn *conn, const char *stmtName);

這是 PQdescribePrepared 的非同步版本:如果它能夠發送請求,則返回 1,否則返回 0。成功呼叫後,請呼叫 PQgetResult 以取得結果。此函式的參數處理方式與 PQdescribePrepared 相同。

PQsendDescribePortal #

提交請求以取得有關指定 Portal 的資訊,而不等待完成。

int PQsendDescribePortal(PGconn *conn, const char *portalName);

這是 PQdescribePortal 的非同步版本:如果它能夠發送請求,則返回 1,否則返回 0。成功呼叫後,請呼叫 PQgetResult 以取得結果。此函式的參數處理方式與 PQdescribePortal 相同。

PQsendClosePrepared #

提交請求以關閉指定的預先處理陳述式,而不等待完成。

int PQsendClosePrepared(PGconn *conn, const char *stmtName);

這是 PQclosePrepared 的非同步版本:如果它能夠發送請求,則返回 1,否則返回 0。成功呼叫後,請呼叫 PQgetResult 以取得結果。此函式的參數處理方式與 PQclosePrepared 相同。

PQsendClosePortal #

提交請求以關閉指定的 Portal,而不等待完成。

int PQsendClosePortal(PGconn *conn, const char *portalName);

這是 PQclosePortal 的非同步版本:如果它能夠發送請求,則返回 1,否則返回 0。成功呼叫後,請呼叫 PQgetResult 以取得結果。此函式的參數處理方式與 PQclosePortal 相同。

PQgetResult #

等待先前 PQsendQueryPQsendQueryParamsPQsendPreparePQsendQueryPreparedPQsendDescribePreparedPQsendDescribePortalPQsendClosePreparedPQsendClosePortalPQsendPipelineSyncPQpipelineSync 呼叫的下一個結果,並傳回它。當命令完成且沒有更多結果時,會傳回一個空指標。

PGresult *PQgetResult(PGconn *conn);

必須重複呼叫 PQgetResult,直到它傳回空指標,表示命令已完成。(如果在沒有活動命令時呼叫,PQgetResult 會立即傳回空指標。)來自 PQgetResult 的每個非空結果都應使用先前描述的相同 PGresult 存取器函式進行處理。完成後,不要忘記使用 PQclear 釋放每個結果物件。請注意,只有在命令處於活動狀態且 PQconsumeInput 尚未讀取必要的回應資料時,PQgetResult 才會被封鎖。

在 Pipeline 模式下,除非發生錯誤,否則 PQgetResult 將正常傳回;對於在導致錯誤的查詢之後傳送的任何後續查詢,直到(且不包括)下一個同步點,將傳回 PGRES_PIPELINE_ABORTED 類型的特殊結果,並且在此之後將傳回空指標。當到達 Pipeline 同步點時,將傳回 PGRES_PIPELINE_SYNC 類型的結果。同步點之後的下一個查詢的結果會立即跟隨(也就是說,在同步點之後不會傳回空指標。)

注意

即使 PQresultStatus 指示發生嚴重錯誤,也應呼叫 PQgetResult 直到它傳回空指標,以便 libpq 能夠完全處理錯誤資訊。

使用 PQsendQueryPQgetResult 解決了 PQexec 的一個問題:如果命令字串包含多個SQL命令,則可以單獨取得這些命令的結果。(順便說一句,這允許一種簡單形式的重疊處理:客戶端可以在伺服器仍在處理同一命令字串中的後續查詢時,處理一個命令的結果。)

另一個常見的需求是,使用 PQsendQueryPQgetResult 可以一次取得有限數量的列的大型查詢結果。這會在第 32.6 節中討論。

單獨呼叫 PQgetResult 仍然會導致用戶端阻塞,直到伺服器完成下一個SQL指令。可以透過正確使用另外兩個函式來避免這種情況

PQconsumeInput #

如果伺服器有可用的輸入,則使用它。

int PQconsumeInput(PGconn *conn);

PQconsumeInput 通常傳回 1,表示沒有錯誤,但如果有任何問題,則傳回 0(在這種情況下,可以查詢 PQerrorMessage)。請注意,結果不會說明是否實際收集到任何輸入資料。在呼叫 PQconsumeInput 之後,應用程式可以檢查 PQisBusy 和/或 PQnotifies 以查看它們的狀態是否已更改。

即使應用程式尚未準備好處理結果或通知,也可以呼叫 PQconsumeInput。該函式將讀取可用資料並將其儲存在緩衝區中,從而使 select() 讀取就緒指示消失。因此,應用程式可以使用 PQconsumeInput 立即清除 select() 條件,然後從容地檢查結果。

PQisBusy #

如果指令忙碌,則傳回 1,也就是說,PQgetResult 會阻塞等待輸入。傳回 0 表示可以呼叫 PQgetResult,並保證不會阻塞。

int PQisBusy(PGconn *conn);

PQisBusy 本身不會嘗試從伺服器讀取資料;因此,必須先呼叫 PQconsumeInput,否則忙碌狀態永遠不會結束。

使用這些函式的典型應用程式將具有一個主迴圈,該迴圈使用 select()poll() 來等待它必須回應的所有條件。其中一個條件是伺服器提供的可用輸入,就 select() 而言,這表示由 PQsocket 識別的檔案描述符上的可讀資料。當主迴圈偵測到輸入就緒時,它應該呼叫 PQconsumeInput 以讀取輸入。然後,它可以呼叫 PQisBusy,如果 PQisBusy 傳回 false (0),則呼叫 PQgetResult。它也可以呼叫 PQnotifies 以偵測 NOTIFY 訊息(請參閱第 32.9 節)。

使用 PQsendQuery/PQgetResult 的用戶端也可以嘗試取消伺服器仍在處理的指令;請參閱第 32.7 節。但是,無論 PQcancelBlocking 的傳回值如何,應用程式都必須使用 PQgetResult 繼續正常的結果讀取順序。成功取消只會導致指令比原本更快終止。

透過使用上述函式,可以避免在等待資料庫伺服器的輸入時發生阻塞。但是,應用程式仍然可能阻塞等待將輸出傳送到伺服器。這種情況相對罕見,但如果傳送非常長的 SQL 指令或資料值,則可能會發生。(但是,如果應用程式透過 COPY IN 傳送資料,則更有可能發生這種情況。)為了防止這種可能性並實現完全非阻塞的資料庫操作,可以使用以下其他函式。

PQsetnonblocking #

設定連線的非阻塞狀態。

int PQsetnonblocking(PGconn *conn, int arg);

如果 arg 為 1,則將連線的狀態設定為非阻塞,如果 arg 為 0,則設定為阻塞。如果正常,則傳回 0,如果發生錯誤,則傳回 -1。

在非阻塞狀態下,成功呼叫 PQsendQueryPQputlinePQputnbytesPQputCopyDataPQendcopy 不會阻塞;它們的變更會儲存在本機輸出緩衝區中,直到刷新為止。不成功的呼叫將傳回錯誤,並且必須重試。

請注意,PQexec 不會遵循非阻塞模式;如果呼叫它,它仍然會以阻塞方式運作。

PQisnonblocking #

傳回資料庫連線的阻塞狀態。

int PQisnonblocking(const PGconn *conn);

如果連線設定為非阻塞模式,則傳回 1,如果為阻塞模式,則傳回 0。

PQflush #

嘗試將任何排隊的輸出資料刷新到伺服器。如果成功(或如果傳送佇列為空),則傳回 0;如果由於某種原因而失敗,則傳回 -1;如果無法傳送傳送佇列中的所有資料(這種情況只會在連線為非阻塞時發生),則傳回 1。

int PQflush(PGconn *conn);

在非阻塞連線上傳送任何指令或資料後,呼叫 PQflush。如果它傳回 1,則等待 socket 變成讀取或寫入就緒。如果它變成寫入就緒,則再次呼叫 PQflush。如果它變成讀取就緒,則呼叫 PQconsumeInput,然後再次呼叫 PQflush。重複此操作,直到 PQflush 傳回 0。(必須檢查讀取就緒並使用 PQconsumeInput 排空輸入,因為伺服器可能會阻塞嘗試向我們傳送資料,例如 NOTICE 訊息,並且在我們讀取它的資料之前不會讀取我們的資料。)一旦 PQflush 傳回 0,則等待 socket 變成讀取就緒,然後按照上述說明讀取回應。

提交更正

如果您在文件中看到任何不正確、與您特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。