PQexec
函式足以在正常的同步應用程式中提交命令。然而,它有一些缺點,對某些使用者來說可能很重要
PQexec
等待命令完成。應用程式可能還有其他工作要做(例如維護使用者介面),在這種情況下,它不希望被阻塞等待回應。
由於客戶端應用程式的執行會在等待結果時暫停,因此應用程式很難決定它是否想要嘗試取消正在進行的命令。(它可以從訊號處理常式中完成,但不能以其他方式完成。)
PQexec
只能傳回一個 PGresult
結構。如果提交的命令字串包含多個SQL命令,則除了最後一個 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
才會被封鎖。
在 Pipeline 模式下,除非發生錯誤,否則 PQgetResult
將正常傳回;對於在導致錯誤的查詢之後傳送的任何後續查詢,直到(且不包括)下一個同步點,將傳回 PGRES_PIPELINE_ABORTED
類型的特殊結果,並且在此之後將傳回空指標。當到達 Pipeline 同步點時,將傳回 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,則設定為阻塞。如果正常,則傳回 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 變成讀取就緒,然後按照上述說明讀取回應。
如果您在文件中看到任何不正確、與您特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。