支援的版本:目前 (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 之外的所有 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 才會阻塞。

在管線模式下,除非發生錯誤,否則 PQgetResult 將正常返回;對於在導致錯誤的查詢之後發送的任何後續查詢,直到(但不包含)下一個同步點,將返回一個 PGRES_PIPELINE_ABORTED 類型的特殊結果,並且在此之後將返回一個空指標。當達到管線同步點時,將返回一個 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,則設定為阻塞狀態。如果 OK 則返回 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 變成可讀狀態,然後如上所述讀取回應。

提交更正

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