PQexec
函式足以用於在一般的同步應用程式中提交指令。然而,它有一些缺點,可能對某些使用者很重要
PQexec
會等待指令完成。應用程式可能還有其他工作要做(例如維護使用者介面),在這種情況下,它不希望阻塞並等待回應。
由於用戶端應用程式的執行在等待結果時會被暫停,因此應用程式很難決定它是否想嘗試取消正在進行的指令。(可以從訊號處理常式完成,但不能以其他方式完成。)
PQexec
只能傳回一個 PGresult
結構。如果提交的指令字串包含多個SQL指令,則除了最後一個 PGresult
之外的所有 PGresult
都會被 PQexec
捨棄。
PQexec
總是收集指令的整個結果,並將其緩衝在單個 PGresult
中。雖然這簡化了應用程式的錯誤處理邏輯,但對於包含許多列的結果而言,這可能是不切實際的。
不喜歡這些限制的應用程式可以改用建構 PQexec
的底層函式:PQsendQuery
和 PQgetResult
。還有 PQsendQueryParams
、PQsendPrepare
、PQsendQueryPrepared
、PQsendDescribePrepared
、PQsendDescribePortal
、PQsendClosePrepared
和 PQsendClosePortal
,可以與 PQgetResult
一起使用,以複製 PQexecParams
、PQprepare
、PQexecPrepared
、PQdescribePrepared
、PQdescribePortal
PQclosePrepared
和 PQclosePortal
的功能。
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
#等待先前 PQsendQuery
、PQsendQueryParams
、PQsendPrepare
、PQsendQueryPrepared
、PQsendDescribePrepared
、PQsendDescribePortal
、PQsendClosePrepared
、PQsendClosePortal
、PQsendPipelineSync
或 PQpipelineSync
呼叫的下一個結果,並返回它。當指令完成且沒有更多結果時,會返回一個空指標。
PGresult *PQgetResult(PGconn *conn);
必須重複呼叫 PQgetResult
,直到它返回空指標,表示指令已完成。(如果在沒有活動指令時呼叫,PQgetResult
會立即返回一個空指標。)來自 PQgetResult
的每個非空結果都應該使用先前描述的相同 PGresult
存取函式進行處理。完成後,別忘了用 PQclear
釋放每個結果物件。請注意,只有在指令處於活動狀態且必要的回應資料尚未被 PQconsumeInput
讀取時,PQgetResult
才會阻塞。
在管線模式下,除非發生錯誤,否則 PQgetResult
將正常返回;對於在導致錯誤的查詢之後發送的任何後續查詢,直到(但不包含)下一個同步點,將返回一個 PGRES_PIPELINE_ABORTED
類型的特殊結果,並且在此之後將返回一個空指標。當達到管線同步點時,將返回一個 PGRES_PIPELINE_SYNC
類型的結果。同步點之後的下一個查詢的結果會立即跟隨(也就是說,在同步點之後不會返回空指標。)
即使 PQresultStatus
指示發生嚴重錯誤,也應該呼叫 PQgetResult
,直到它返回空指標,以便 libpq 能夠完全處理錯誤資訊。
使用 PQsendQuery
和 PQgetResult
解決了 PQexec
的一個問題:如果指令字串包含多個SQL指令,則可以單獨取得這些指令的結果。(順帶一提,這允許一種簡單形式的重疊處理:客戶端可以在伺服器仍在處理相同指令字串中的後續查詢時,處理一個指令的結果。)
另一個常被要求的功能是使用 PQsendQuery
和 PQgetResult
一次只檢索有限數量的列來取得大型查詢結果。這會在第 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。
在非阻塞狀態下,成功呼叫 PQsendQuery
、PQputline
、PQputnbytes
、PQputCopyData
和 PQendcopy
將不會阻塞;它們的變更會儲存在本機輸出緩衝區中,直到它們被刷新。不成功的呼叫將返回錯誤,並且必須重試。
請注意,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 變成可讀狀態,然後如上所述讀取回應。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符,或需要進一步澄清的地方,請使用此表單回報文件問題。