?
This document uses PHP Chinese website manual Release
PQexec
函數(shù)對普通的同步應(yīng)用里提交命令已經(jīng)是足夠用的了。但是它卻有幾個主要的缺陷,
而這些缺陷可能對某些用戶很重要:
PQexec
等待命令結(jié)束。而應(yīng)用可能還有其它的工作要做(比如維護用戶界面等),
這個時候它可不想阻塞在這里等待相應(yīng)。
因為客戶端應(yīng)用在等待結(jié)果的時候是處于掛起狀態(tài)的,所以應(yīng)用很難判斷它是否該嘗試結(jié)束正在進行的命令。 (這個事情可以在一個信號句柄中做,但是沒別的方法。)
PQexec
只能返回一個PGresult結(jié)構(gòu)。如果提交的命令字符串包含多個SQL命令,
除了最后一個PGresult以外都會被 PQexec 丟棄。
不想受到這些限制的應(yīng)用可以改用下面的函數(shù),這些函數(shù)也是構(gòu)造PQexec
的函數(shù):
也有PQsendQueryParams
,
PQsendPrepare
,
PQsendQueryPrepared
,
PQsendDescribePrepared
和
PQsendDescribePortal
,
他們可以和PQgetResult
一起使用,
分別用于復(fù)制PQexecParams
,
PQprepare
,
PQexecPrepared
,
PQdescribePrepared
和
PQdescribePortal
的功能。
PQsendQuery
向服務(wù)器提交一個命令而不等待結(jié)果。如果查詢成功發(fā)送則返回 1,否則返回 0。
(此時,可以用PQerrorMessage
獲取關(guān)于失敗的信息)。
int PQsendQuery(PGconn *conn,const char *command);
在成功調(diào)用PQsendQuery
后,調(diào)用PQgetResult
一次或者多次獲取結(jié)果
。在PQsendQuery
返回 NULL 指針,表明命令完成之前,
我們不能再調(diào)用PQsendQuery
(在同一次連接里)。
PQsendQueryParams
給服務(wù)器提交一個命令和(命令需要的)分隔的參數(shù),而不等待結(jié)果。
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
,只是查詢參數(shù)可以和查詢字串分開聲明。
函數(shù)的參數(shù)處理和PQexecParams
一樣。和PQexecParams
類似,它不能在 2.0 版本的協(xié)議連接上工作,
并且它只允許在查詢字串里出現(xiàn)一條命令。
PQsendPrepare
發(fā)送一個請求,創(chuàng)建一個給定參數(shù)的準(zhǔn)備好語句,而不等待結(jié)束。
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);
這是PQprepare
的異步版本:如果它能發(fā)送這個請求,則返回 1,如果不能,則返回 0。
在成功調(diào)用之后,調(diào)用PQgetResult
判斷服務(wù)器是否成功創(chuàng)建了準(zhǔn)備好語句。
這個函數(shù)的參數(shù)的處理和PQprepare
一樣。類似PQprepare
,
它不能在 2.0 版本協(xié)議的連接上運轉(zhuǎn)。
PQsendQueryPrepared
發(fā)送一個執(zhí)行帶有給出參數(shù)的準(zhǔn)備好的語句的請求,不等待結(jié)果。
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);
這個函數(shù)類似PQsendQueryParams
,但是要執(zhí)行的命令是通過給一個前面準(zhǔn)備好的語句命名來聲明的,
而不是給出一個查詢字串。函數(shù)的參數(shù)處理和PQexecPrepared
一樣。類似 PPQexecPrepared
,
它也不能在 2.0 版本的協(xié)議連接上跑。
PQsendDescribePrepared
發(fā)送一個執(zhí)行帶有給出參數(shù)的準(zhǔn)備好的語句的請求,不等待結(jié)果。
int PQsendDescribePrepared(PGconn *conn,const char *stmtName);
這個函數(shù)類似PQdescribePrepared
,但是要執(zhí)行的命令是通過給一個前面準(zhǔn)備好的語句命名來聲明的,
而不是給出一個查詢字串。函數(shù)的參數(shù)處理和PQgetResult
一樣。類似PQdescribePrepared
,
它也不能在 2.0 版本的協(xié)議連接上跑。
PQsendDescribePortal
發(fā)出請求,以獲得關(guān)于制定端口的信息,不需要等待完成。
int PQsendDescribePortal(PGconn *conn,const char *portalName);
這是一個PQdescribePortal
的異步版本:
如果調(diào)度請求,那么返回1,否則返回0。成功調(diào)用之后,通過PQgetResult
獲得結(jié)果。
函數(shù)參數(shù)處理與PQdescribePortal
相同。
類似于PQdescribePortal
,不能在2.0的協(xié)議連接上工作。
PQgetResult
等待從前面PQsendQuery
,
PQsendQueryParams
,
PQsendPrepare
或者PQsendQueryPrepared
調(diào)用返回的下一個結(jié)果,然后返回之。
當(dāng)命令結(jié)束并且沒有更多結(jié)果后返回 NULL。
PGresult *PQgetResult(PGconn *conn);
必須重復(fù)的調(diào)用PQgetResult
,直到它返回 NULL,表明該命令結(jié)束。(如果在沒有活躍的命令時調(diào)用,
PQgetResult
將只是立即返回一個空指針。) 每個PQgetResult
返回的非 NULL 結(jié)
果都應(yīng)該用前面描述的PGresult訪問函數(shù)進行分析。不要忘了在結(jié)束分析后用PQclear
釋放每個結(jié)果對象。注意,PQgetResult
只是在有一個命令是活躍的而且必須的返回數(shù)據(jù)還沒
有被PQconsumeInput
讀取時阻塞。
使用PQsendQuery
和PQgetResult
解決了PQexec
的一個問題:
如果一個命令字符串包含多個 SQL 命令,
這些命令的結(jié)果可以獨立的獲得。(順便說一句:這樣就允許一種簡單的重疊處理模式,
客戶端可以處理一個命令的結(jié)果而服務(wù)器可以仍然在處理同一命令字符串的后面的查詢。)
但是,調(diào)用PQgetResult
將仍然導(dǎo)致前端被阻塞住直到服務(wù)器完成下一個SQL命令。
這一點可以通過合理的使用下面兩個函數(shù)來避免:
PQconsumeInput
如果存在服務(wù)器來的輸入可用,則使用之。
int PQconsumeInput(PGconn *conn);
PQconsumeInput
通常返回 1 表明"no error",而返回 0 表明有某種錯誤發(fā)生,
(這個時候可以用PQerrorMessage
)。注意這個結(jié)果并不表明實際上是否收集了數(shù)據(jù)。
在調(diào)用PQconsumeInput
,之后,應(yīng)用可以檢查PQisBusy
和/或PQnotifies
看一眼它們的狀態(tài)是否改變。
PQconsumeInput
可以在應(yīng)用還沒有做好處理結(jié)果或通知的情況下被調(diào)用。這個函數(shù)將讀取可用的數(shù)
據(jù)并且在一個緩沖區(qū)里保存它,這樣導(dǎo)致一個select()
讀準(zhǔn)備好標(biāo)識的生成。這樣應(yīng)用就可以使用
PQconsumeInput
立即清掉select()
條件,然后在空閑的時候檢查結(jié)果。
PQisBusy
在查詢忙的時候返回 1 ,也就是說,PQgetResult
將阻塞住等待輸入。
一個 0 的返回表明這時調(diào)用PQgetResult
等以確保不阻塞。
int PQisBusy(PGconn *conn);
PQisBusy
本身將不會試圖從服務(wù)器讀取數(shù)據(jù);所以必須先調(diào)用
PQconsumeInput
,否則忙狀態(tài)將永遠不會消除。
一個使用這些函數(shù)的典型的應(yīng)用將有一個主循環(huán)使用select()
或poll()
等待所有它必須處理的條件。
其中一個條件將會是服務(wù)器來的數(shù)據(jù)已準(zhǔn)備好,從select()
的角度
來看就是 PQsocket 標(biāo)識的文件描述符上已經(jīng)有可讀取的數(shù)據(jù)。當(dāng)主循環(huán)
偵測到輸入準(zhǔn)備好,它將調(diào)用PQconsumeInput
讀取輸入。然后可以調(diào)用PQisBusy
返回
false (0)后面可以跟著PQgetResult
。同樣它(用戶應(yīng)用)可以調(diào)用PQnotifies
檢測
NOTIFY信息(參閱下面的Section 31.7)。
一個使用PQsendQuery
//PQgetResult
的客戶端同樣也可以試圖取消一個正在被服務(wù)
器處理的命令。參閱Section 31.5.。但是,不管PQcancel
,返回的值是多少,
應(yīng)用都必須使用PQgetResult
進行正常的讀取結(jié)果的動作序列。
一次成功的取消只會導(dǎo)致命令比正常情況下快些結(jié)束。
通過使用上面描述的函數(shù),我們可以避免在等待來自數(shù)據(jù)庫服務(wù)器的時候的阻塞。不過, 應(yīng)用還是有可能阻塞在給服務(wù)器發(fā)送輸出上。這種情況比較少見,但是也可能發(fā)生, 尤其是我們要發(fā)送非常長的 SQL 命令或者數(shù)據(jù)值的時候。(不過,最有可能的是在應(yīng)用通 過COPY IN發(fā)送數(shù)據(jù)的時候。)為了避免這個可能性,實現(xiàn)完全的非阻塞數(shù)據(jù)庫操作 ,我們可以使用下列額外的函數(shù)。
PQsetnonblocking
把連接的狀態(tài)設(shè)置為非阻塞。
int PQsetnonblocking(PGconn *conn,int arg);
如果arg為 1,把連接狀態(tài)設(shè)置為非阻塞,如果arg為 0,把連接狀態(tài)設(shè) 置為阻塞。如果 OK 返回 0,如果錯誤返回 -1。
In the nonblocking state,calls to
PQsendQuery
,PQputline
,
PQputnbytes
,and
PQendcopy
will not block but instead return
an error if they need to be called again.
在非阻塞狀態(tài),調(diào)用PQsendQuery
,PQputline
,
PQputnbytes
,和
PQendcopy
的時候不被阻塞,
而是在如果需要再次調(diào)用它們時將返回一個錯誤。
請注意PQexec
不會在意任何非阻塞模式;如果調(diào)用了PQexec
,那么它的行為總是阻塞的。
PQisnonblocking
返回數(shù)據(jù)庫連接的阻塞狀態(tài)。
int PQisnonblocking(const PGconn *conn);
如果連接設(shè)置為非阻塞狀態(tài),返回 1,如果是阻塞狀態(tài)返回 0。
PQflush
試圖把任何正在排隊的數(shù)據(jù)沖刷到服務(wù)器,如果成功(或者發(fā)送隊列為空)返回 0, 如果因某種原因失敗返回 -1,或者是在無法把發(fā)送隊列中的所有數(shù)據(jù)都發(fā)送出去, 返回 1。(這種情況只有在連接不為阻塞模式的時候才會出現(xiàn))。
int PQflush(PGconn *conn);
在一個非阻塞的連接上發(fā)送任何命令或者數(shù)據(jù)之后,調(diào)用PQflush
。如果返回 1,
就等待套接字寫準(zhǔn)備好然后再次調(diào)用;重復(fù)這個操作直到它返回 0。一旦PQflush
返回 0,
則等待套接字為讀準(zhǔn)備好,準(zhǔn)備好之后就像上面那樣讀取。