亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數(shù)據(jù)庫 > PostgreSQL > 正文

PostgreSQL7.0手冊-接口-53. libpq - C 庫

2019-09-08 23:34:13
字體:
供稿:網(wǎng)友
第五十三章. libpq - C 庫
內(nèi)容 
數(shù)據(jù)庫聯(lián)接函數(shù) 
查詢執(zhí)行函數(shù) 
異步查詢處理 
捷徑 
異步通知 
與 COPY 命令相關(guān)的函數(shù) 
libpq 跟蹤函數(shù) 
libpq 控制函數(shù) 
環(huán)境變量 
線程特性 
例子程序 
libpq 是 Postgres 的 C 應(yīng)用程序員的接口.libpq 是一套允許客戶程序向 Postgres 后端服務(wù)進(jìn)程發(fā)送查詢并且獲得查詢返回的庫過程.libpq 同時也是其他幾個 Postgres 應(yīng)用接口下面的引擎,包括 libpq++?。–++),libpgtcl?。═cl), perl5,和 ecpg.所以如果你使用這些軟件包,libpq 某些方面的特性會對你非常重要. 
本節(jié)末尾有三個小程序顯示如何利用 libpq 書寫程序.在下面目錄里面有幾個完整的 libpq 應(yīng)用的例子: 
  

../src/test/regress
../src/test/examples
../src/bin/psql
使用 libpq 的前端程序必須包括頭文件 libpq-fe.h 并且必須與 libpq 庫鏈接. 
  
數(shù)據(jù)庫聯(lián)接函數(shù)
下面的過程處理與 Postgres 后端服務(wù)器聯(lián)接的事情.一個應(yīng)用程序一次可以與多個后端建立聯(lián)接.(這么做的原因之一是訪問多于一個數(shù)據(jù)庫.)每個連接都是用一個從 PQconnectdb() 或 PQsetdbLogin()獲得的 PGconn 對象表示.注意,這些函數(shù)總是返回一個非空的對象指針,除非存儲器少得連個 PGconn 對象都分配不出來.在把查詢發(fā)送給聯(lián)接對象之前,可以調(diào)用 PQstatus 函數(shù)來檢查一下聯(lián)接是否成功. 
PQconnectdb 與后端數(shù)據(jù)庫服務(wù)器建立一個新的聯(lián)接. 
PGconn *PQconnectdb(const char *conninfo)
這個過程用從一個字符串 conninfo 來的參數(shù)與數(shù)據(jù)庫打開一個新的聯(lián)接.與下面的 PQsetdbLogin() 不同的是,我們可以不必更換函數(shù)簽名(名字)就可以擴(kuò)展參數(shù)集,所以我們建議應(yīng)用程序中使用這個函數(shù)或者是它的非阻塞的相似函數(shù) PQconnectStart / PQconnectPoll.傳入的參數(shù)可以為空,表明使用所有缺省的參數(shù),或者可以包含一個或更多個用空白間隔的參數(shù)設(shè)置. 
每個參數(shù)設(shè)置以 關(guān)鍵字=數(shù)值 keyword = value 的形式設(shè)置.(要寫一個空值或者一個包含空白的值,你可以用一對單引號包圍它們,例如,keyword = 'a value'?。?dāng)?shù)值內(nèi)部的單引號必須書寫為 /'.等號周圍的空白是可選的.)目前可識別的參數(shù)值是: 

host 
要聯(lián)接的主機(jī)(host?。绻暶髁艘粋€非零長字符串,就將使用 TCP/IP 通訊.使用這個參數(shù)導(dǎo)致一個主機(jī)名的查找。參閱 hostaddr?!?
hostaddr 
與之聯(lián)接的主機(jī)的 IP 地址。這個可以是標(biāo)準(zhǔn)的數(shù)字-點的形式,象在 BSD 函數(shù) inet_aton 等里面用的那樣。如果聲明了一個非零長的字符串,那么使用 TCP/IP 通訊機(jī)制?!?
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

使用 hostaddr 取代 host 允許應(yīng)用避免一次主機(jī)名查找,這一點對于那些有時間約束的應(yīng)用來說可能是非常重要的。不過,Kerberos 認(rèn)證系統(tǒng)要求主機(jī)(host)名。因此,應(yīng)用下面的規(guī)則。如果聲明了不帶 hostaddr 的 host 那么就強(qiáng)制進(jìn)行主機(jī)名查找。如果聲明中沒有 host,hostaddr 的值給出遠(yuǎn)端的地址;如果使用了 Kerberos,將導(dǎo)致一次反向名字查詢。如果同時聲明了 host 和 hostaddr,除非使用了 Kerberos,否則將使用 hostaddr 的值作為遠(yuǎn)端地址;host 的值將被忽略,如果使用了 Kerberos,host 的值用于 Kerberos 認(rèn)證。要注意如果傳遞給 libpq 的主機(jī)名(host)不是地址 hostaddr 處的機(jī)器名,那么認(rèn)證很有可能失敗。 

如果主機(jī)名(host)和主機(jī)地址都沒有,那么 libpq 將使用一個本地的 Unix 域套接字進(jìn)行通訊?!?

port 
主機(jī)服務(wù)器的端口號,或者在 Unix 主控套接字聯(lián)接時的套接字?jǐn)U展文件名. 
dbname 
數(shù)據(jù)庫名. 
user 
要聯(lián)接的用戶名?!?
password 
如果后端要求口令認(rèn)證,所用的口令. 
options 
發(fā)給后端的跟蹤/調(diào)試選項. 
tty 
文件或控制臺,用于從后端的可選調(diào)試輸出. 
如果有任何沒有聲明的參數(shù),那么將檢查對應(yīng)的環(huán)境變量(參閱"環(huán)境變量"章)。如果環(huán)境變量也沒有設(shè)置,那么使用編譯時的硬代碼。返回的值是一個指向代表與后端聯(lián)接的抽象結(jié)構(gòu)的指針?!?
PQsetdbLogin 與后端數(shù)據(jù)庫服務(wù)器建立一個新的聯(lián)接. 

PGconn *PQsetdbLogin(const char *pghost,
                     const char *pgport,
                     const char *pgoptions,
                     const char *pgtty,
                     const char *dbName,
                     const char *login,
                     const char *pwd)
這個函數(shù)是 PQconnectdb 前身,它有固定個數(shù)的參數(shù),但是有相同的功能?!?
PQsetdb 與后端數(shù)據(jù)庫服務(wù)器建立一個新的聯(lián)接. 

PGconn *PQsetdb(char *pghost,
                char *pgport,
                char *pgoptions,
                char *pgtty,
                char *dbName)
這是一個調(diào)用 PQsetdbLogin() 的宏,只是 login 和 pwd 參數(shù)用空(null )代替.提供這個函數(shù)主要是為了與老版本的程序兼容. 
PQconnectStart PQconnectPoll 與數(shù)據(jù)庫服務(wù)器建立一次非阻塞的聯(lián)接?!?

PGconn *PQconnectStart(const char *conninfo)
PostgresPollingStatusType *PQconnectPoll(PQconn *conn)
著兩個過程用于打開一個與數(shù)據(jù)庫服務(wù)器之間的非阻塞的聯(lián)接:你的應(yīng)用的執(zhí)行線索在運(yùn)行的時候不會阻塞遠(yuǎn)端的 I/O?!?
數(shù)據(jù)庫聯(lián)接是用從 conninfo 字符串里取得的參數(shù)傳遞給 PQconnectStart 進(jìn)行的。這個字符串的格式與上面 PQconnectdb 里描述的一樣?!?

PQconnectStart 和 PQconnectPoll 都不會阻塞(進(jìn)程),不過有一些限制: 
  

必須正確提供 hostaddr 和 host 參數(shù)以確保不會發(fā)生正向或者反向的名字查找。參閱上面 PQconnectdb 里的這些參數(shù)的文檔獲取細(xì)節(jié)?!?
如果你調(diào)用了 PQtrace,確保你跟蹤進(jìn)入的流對象不會阻塞?!?

你必須在調(diào)用 PQconnectPoll 之前確?!ocket 處于正確的狀態(tài),象下面描述的那樣。

要開始(聯(lián)接),調(diào)用 conn=PQconnectStart("")。如果 conn 是 NULL,表明 libpq 無法分配一個新的 PGconn 結(jié)構(gòu)。否則,返回一個有效的 PGconn 指針(盡管還不一定代表一個與數(shù)據(jù)庫有效聯(lián)接)。PQconnectStart 一返回,調(diào)用 status=PQstatus(conn)。如果 status 等于 CONNECTION_BAD,PQconnectStart 失敗?!?
如果 PQconnectStart 成功了,下一個階段是輪詢 libpq,這樣它就可以繼續(xù)進(jìn)行后繼的聯(lián)接動作。象這樣循環(huán):認(rèn)為一個聯(lián)接缺省時是'不活躍'的。如果 PQconnectPoll 的最后一個返回是PGRES_POLLING_ACTIVE,則認(rèn)為它是'活躍的'。如果 PQconnectPoll(conn) 的最后一個返回是PGRES_POLLING_READING,執(zhí)行一個對 PQsocket(conn) 的讀 select。如果最后一個返回是PGRES_POLLING_WRITING,執(zhí)行一個對 PQsocket(conn) 的寫 select。如果還沒(?)調(diào)用 PQconnectPoll,例如象在調(diào)用完 PQconnectStart 后那樣,把它當(dāng)作最后返回PGRES_POLLING_WRITING 那樣對待。如果 select 顯示 socket 已經(jīng)準(zhǔn)備好,那么認(rèn)為它是'活躍的'。如果認(rèn)為一個聯(lián)接是'活躍的',再次調(diào)用 PQconnectPoll(conn)。如果這次調(diào)用返回 PGRES_POLLING_FAILED,聯(lián)接過程失敗,如果這次調(diào)用返回 PGRES_POLLING_OK,聯(lián)接成功?!?

要注意上面用 select() 來確保一個 socket 準(zhǔn)備好只是一個(近似)的例子;還可以用其他方法,比如一個 poll() 調(diào)用,來代替 select。 

在聯(lián)接的任何時候,我們都可以通過調(diào)用 PQstatus 來檢查聯(lián)接的狀態(tài)。如果這是 CONNECTION_BAD,那么聯(lián)接過程失??;如果是 CONNECTION_OK,那么聯(lián)接已經(jīng)做好。著兩種狀態(tài)同樣也可以從上面的 PQconnectPoll 的返回值里檢測到。其他狀態(tài)可能(也只能)在一次異步聯(lián)接過程中看到。這些標(biāo)識聯(lián)接過程的當(dāng)前狀態(tài),因而可能對給用戶提供反饋有幫助。這些狀態(tài)可能包括: 

CONNECTION_STARTED:等待進(jìn)行聯(lián)接。
CONNECTION_MADE:聯(lián)接成功;等待發(fā)送?!?
CONNECTION_AWAITING_RESPONSE:等待來自 postmaster 的響應(yīng)?!?

CONNECTION_AUTH_OK:已收到認(rèn)證;等待后端啟動?!?

CONNECTION_SETENV:協(xié)商環(huán)境。

注意,盡管這些常量將保持下去(為了維持兼容性),應(yīng)用決不應(yīng)該依賴于這些常量的某種特定順序,或者是根本不應(yīng)依賴于這些常量,或者是不應(yīng)該依賴于這些狀態(tài)總是某個文檔聲明的值。一個應(yīng)用可能象象下面這樣: 
    switch(PQstatus(conn))
    {
        case CONNECTION_STARTED:
            feedback = "Connecting...";
            break;

        case CONNECTION_MADE:
            feedback = "Connected to server...";
            break;
.
.
.
        default:
            feedback = "Connecting...";
    }
要注意如果 PQconnectStart 返回一個非空的指針,你必須在使用完它(指針)之后調(diào)用 PQfinish,以處理那些結(jié)構(gòu)和所有相關(guān)的存儲塊。甚至調(diào)用 PQconnectStart 或者 PQconnectPoll 失敗時也要這樣處理。 
如果編譯 libpq 時定義了 USE_SSL 那么目前的 PQconnectPoll 將阻塞住。這個限制可能在將來的版本移除?!?

除非編譯 libpq 時定義了 WIN32_NON_BLOCKING_CONNECTIONS,否則目前的 PQconnectPoll 將在 Windows 里阻塞住。這些代碼還沒有在 Windows 里測試過,因此目前缺省時是不帶這些代碼的。著一點以后可能修改?!?

這些函數(shù)把 socket 置于一個非阻塞的狀態(tài),就好象調(diào)用了 PQsetnonblocking 一樣。 

PQconndefaults 返回缺省的聯(lián)接選項。 

PQconninfoOption *PQconndefaults(void)

struct PQconninfoOption
{
    char   *keyword;   /* The keyword of the option */
    char   *envvar;    /* Fallback environment variable name */
    char   *compiled;  /* Fallback compiled in default value */
    char   *val;       /* Option's current value, or NULL */
    char   *label;     /* Label for field in connect dialog */
    char   *dispchar;  /* Character to display for this field
                          in a connect dialog. Values are:
                          ""        Display entered value as is
                          "*"       Password field - hide value
                          "D"       Debug option - don't show by default */
    int     dispsize;  /* Field size in characters for dialog */
}
返回聯(lián)接選項結(jié)構(gòu)的地址.可以用于獲取所有可能的 PQconnectdb 選項和它們的當(dāng)前缺省值.返回值指向一個 PQconninfoOption 結(jié)構(gòu)數(shù)組,該數(shù)組以一個有 NULL 關(guān)鍵字指針的入口結(jié)束.注意缺省值("val" 域)將依賴于環(huán)境變量和其他上下文.調(diào)用者必須把聯(lián)接選項當(dāng)作只讀對待. 
在處理完選項數(shù)組后,把數(shù)組交給 PQconninfoFree() 釋放.如果沒有這么做,每次調(diào)用 PQconndefaults() 都會有一小部分內(nèi)存泄漏. 

在 Postgres 7.0 以前的版本,PQconndefaults() 返回一個指向靜態(tài)數(shù)組的指針,而不是一個動態(tài)分配的數(shù)組.這樣做是線程不安全的,因此這個特點被修改了. 

PQfinish 關(guān)閉與后端的聯(lián)接.同時釋放被 PGconn 對象使用的存儲器. 

void PQfinish(PGconn *conn)
注意,即使與后端的聯(lián)接嘗試失敗(可由 PQstatus 判斷),應(yīng)用也要調(diào)用 PQfinish 釋放被 PGconn 對象使用的存儲器.PGconn 指針不應(yīng)該在調(diào)用 PQfinish 后再使用. 
PQreset 重置與后端的通訊端口. 

void PQreset(PGconn *conn)
此函數(shù)將關(guān)閉與后端的聯(lián)接并且試圖與同一個 postmaster 重建新的聯(lián)接,使用所有前面使用過的參數(shù).這在失去工作聯(lián)接后進(jìn)行故障恢復(fù)時很有用. 
PQresetStart PQresetPoll 重置與后端的非阻塞模式的通訊端口?!?

int PQresetStart(PGconn *conn);
PostgresPollingStatusType PQresetPoll(PGconn *conn);
此函數(shù)將關(guān)閉與后端的聯(lián)接并且試圖與同一個 postmaster 重建新的聯(lián)接,使用所有前面使用過的參數(shù).這在失去工作聯(lián)接后進(jìn)行故障恢復(fù)時很有用.它們和上面的 PQreset (above) 的區(qū)別是它們工作在非阻塞模式。這些函數(shù)的使用有與上面 PQconnectStart 和 PQconnectPoll 一樣的限制?!?
調(diào)用 PQresetStart。如果它返回 0,那么重置失敗。如果返回 1,用與使用 PQconnectPoll 建立聯(lián)接的同樣的方法使用 PQresetPoll 重置聯(lián)接。

libpq 應(yīng)用程序員應(yīng)該仔細(xì)維護(hù) PGconn 結(jié)構(gòu).使用下面的訪問函數(shù)來獲取 PGconn 的內(nèi)容.避免直接引用 PGconn 結(jié)構(gòu)里的字段,因為這些字段在今后可能被改變.(從PostgreSQL 版本 6.4 開始, 結(jié)構(gòu) PGconn 的定義甚至沒有放在 libpq-fe.h 里.如果你有一些直接訪問 PGconn 數(shù)據(jù)域的舊代碼,你可以通過包含 libpq-int.h 來訪問它們,但我們鼓勵你趕快修改那些代碼.) 
PQdb 返回聯(lián)接的數(shù)據(jù)庫名. 
char *PQdb(const PGconn *conn)
PQdb 和下面幾個函數(shù)返回聯(lián)接后建立起來的幾個值.這些值在 PGconn 對象的生存期內(nèi)是固定的. 
PQuser 返回聯(lián)接的用戶名. 

char *PQuser(const PGconn *conn)
PQpass 返回聯(lián)接的口令. 
char *PQpass(const PGconn *conn)
PQhost 返回聯(lián)接的服務(wù)器主機(jī)名. 
char *PQhost(const PGconn *conn)
PQport 返回聯(lián)接的端口號. 
char *PQport(const PGconn *conn)
PQtty 返回聯(lián)接的調(diào)試控制臺( tty?。?
char *PQtty(const PGconn *conn)
PQoptions 返回聯(lián)接中使用的后端選項. 
char *PQoptions(const PGconn *conn)
PQstatus 返回聯(lián)接的狀態(tài). 
ConnStatusType PQstatus(const PGconn *conn)
這個狀態(tài)可以是一些值之一。不過,在一次異步聯(lián)接過程以外的范圍里只能看到其中的兩個 - CONNECTION_OK 或者 CONNECTION_BAD。一個與數(shù)據(jù)庫的成功的聯(lián)接返回狀態(tài) CONNECTION_OK。一次失敗的企圖用狀態(tài) CONNECTION_BAD 標(biāo)識。通常,一個 OK 狀態(tài)保持到 PQfinish,但是一個通訊失敗可能會導(dǎo)致狀態(tài)永久的改變?yōu)椤ONNECTION_BAD。這時應(yīng)用可以試著調(diào)用 PQreset 來恢復(fù). 
參閱 PQconnectStart 和 PQconnectPoll 條目看看可能出現(xiàn)的其他的狀態(tài)碼?!?

PQerrorMessage 返回聯(lián)接中操作產(chǎn)生的最近的錯誤信息. 

char *PQerrorMessage(const PGconn* conn);
幾乎所有 libpq 函數(shù)在失敗時都會設(shè)置 PQerrorMessage?。⒁狻ibpq 的傳統(tǒng)是,一個非空的 PQerrorMessage 將在結(jié)尾包含一個新行. 
PQbackendPID 返回控制此聯(lián)接的后端服務(wù)器的進(jìn)程號(ID)?!?

int PQbackendPID(const PGconn *conn);
這個后端 PID 在調(diào)試和對比 NOTIFY 信息(包含發(fā)出通知的后端的 PID?。r很有用.注意該 PID 屬于運(yùn)行數(shù)據(jù)庫服務(wù)器的主機(jī)的進(jìn)程,而不是本地主機(jī)!

--------------------------------------------------------------------------------  --------------------------------------------------------------------------------

異步查詢處理
PQexec 函數(shù)對簡單的同步應(yīng)用里提交查詢已經(jīng)是足夠用的了.但是它卻有幾個主要的缺陷: 
  
PQexec 等待查詢結(jié)束.應(yīng)用可能有其他工作要做(例如維護(hù)用戶界面),這時它可不希望阻塞在這里等待返回. 
因為控制是藏在 PQexec 內(nèi)部,前端很難取消掉正進(jìn)行著的查詢.(可以通過信號控制器進(jìn)行,但沒有別的方法.) 

PQexec 只能返回一個 PGresult 結(jié)構(gòu).如果提交的查詢字符串包含多個 SQL 命令,除了最后一個 PGresult 以外都會被 PQexec 丟棄。

不想受到這些限制的應(yīng)用可以改用下面的函數(shù),這些函數(shù)也是構(gòu)造 PQexec 的函數(shù):PQsendQuery 和PQgetResult?!?
使用這些(異步)功能以及 PQputline 和 PQputnbytes 的老一些的程序可能在等待數(shù)據(jù)發(fā)送給后端時阻塞住,要改變這樣的方式,增加了函數(shù) PQsetnonblocking?!?

舊應(yīng)用可以忽略 PQsetnonblocking 的使用,維持原有的阻塞特征。新的程序可以利用 PQsetnonblocking 獲得與后端完全非阻塞的聯(lián)接?!?

PQsetnonblocking 把該聯(lián)接的狀態(tài)設(shè)置為非阻塞?!?
int PQsetnonblocking(PGconn *conn)
此函數(shù)將確保對 PQputline,PQputnbytes,PQsendQuery 和 PQendcopy 的調(diào)用不被阻塞,如果對它們的調(diào)用需要再次執(zhí)行將是返回一個錯誤(而不是阻塞)?!?
當(dāng)把一個數(shù)據(jù)庫的聯(lián)接設(shè)置為非阻塞的模式并且調(diào)用了 PQexec,它將暫時把聯(lián)接狀態(tài)設(shè)置為阻塞模式直到 PQexec 完成?!?

在不久的將來將有更多的 libpq 會設(shè)計成在 PQsetnonblocking 方式下是安全的?!?

PQisnonblocking 返回數(shù)據(jù)庫聯(lián)接的阻塞狀態(tài)?!?

int PQisnonblocking(const PGconn *conn)
如果聯(lián)接設(shè)置為非阻塞狀態(tài),返回 TRUE,如果是阻塞狀態(tài)返回 FALSE。 
PQsendQuery 向 Postgres 提交一個查詢而不等待結(jié)果.如果查詢成功發(fā)送則返回真(TRUE?。?,否則返回假(FALSE)(此時,可以用 PQerrorMessage 獲取關(guān)于失敗的信息). 

int PQsendQuery(PGconn *conn,
                const char *query);
在成功調(diào)用 PQsendQuery 后,調(diào)用 PQgetResult 一次或者多次獲取查詢結(jié)果.PQsendQuery 可以不再調(diào)用(在同一次聯(lián)接里)直到 PQgetResult 返回 NULL,表明查詢完成. 
PQgetResult 等待從前面 PQsendQuery 調(diào)用返回的下一個結(jié)果,然后返回之.當(dāng)查詢結(jié)束并且沒有更多結(jié)果后返回 NULL. 

PGresult *PQgetResult(PGconn *conn);
PQgetResult 必須重復(fù)的調(diào)用,直到它返回 NULL,表明該查詢結(jié)束.(如果在沒有激活查詢時調(diào)用, PQgetResult 將只是立即返回 NULL.)每個 PQgetResult返回的非空結(jié)果都應(yīng)該用前面描述的 PGresult 訪問函數(shù)進(jìn)行分析.不要忘了在結(jié)束分析后用 PQclear 釋放每個結(jié)果對象.注意,PQgetResult 只是在有查詢激活而且必須的返回數(shù)據(jù)還沒有被 PQconsumeInput 讀取時阻塞.
使用 PQsendQuery 和 PQgetResult 解決了 PQexec 的一個問題:如果一個查詢字符串包含多個 SQL 命令,這些命令的結(jié)果可以獨(dú)立的獲得.(順便說一句:這樣就允許一種簡單的重疊處理模式,前端可以處理一個查詢的結(jié)果而后端可以仍然在處理同一查詢字符串的后面的查詢.)但是,調(diào)用 PQgetResult 將仍然導(dǎo)致前端被阻塞住直到后端完成下一個SQL 命令.這一點可以通過合理的使用下面三個函數(shù)來避免: 
PQconsumeInput 如果存在后端來的輸入可用,則使用之. 
int PQconsumeInput(PGconn *conn);
PQconsumeInput 通常返回 1 表明"沒有錯誤",而返回 0 表明有某種錯誤發(fā)生(同時設(shè)置 PQerrorMessage?。⒁膺@個結(jié)果并不表明實際上是否收集了數(shù)據(jù).在調(diào)用 PQconsumeInput 之后,應(yīng)用可以檢查 PQisBusy 和/或 PQnotifies 看一眼它們的狀態(tài)是否改變. 
PQconsumeInput 可以在應(yīng)用還沒有做好處理結(jié)果或通知的情況下被調(diào)用.這個過程將讀取可用的數(shù)據(jù)并且在一個緩沖區(qū)里保存它,這樣導(dǎo)致一個 select(2) 讀準(zhǔn)備好標(biāo)識的生成.這樣應(yīng)用就可以使用PQconsumeInput 立即清掉 select 條件,然后在空閑的時候檢查結(jié)果. 

PQisBusy 在查詢忙的時候返回 1 ,也就是說,PQgetResult 將阻塞住等待輸入.一個 0 的返回表明這時調(diào)用 PQgetResult 可以確保不阻塞. 

int PQisBusy(PGconn *conn);
PQisBusy 本身將不會試圖從后端讀取數(shù)據(jù);所以必須先調(diào)用 PQconsumeInput ,否則忙狀態(tài)將永遠(yuǎn)不會消除. 
PQflush 試圖把任何正在排隊的數(shù)據(jù)沖刷到后端,如果成功(或者發(fā)送隊列為空)返回 0,如果因某種原因失敗返回 EOF?!?

int PQflush(PGconn *conn);
在一個非阻塞的聯(lián)接調(diào)用 select 判斷是否有響應(yīng)到達(dá)之前需要調(diào)用一個 PQflush。如果返回 0 則保證了與后端的發(fā)送隊列里面沒有待發(fā)送的數(shù)據(jù)。只有使用了 PQsetnonblocking 的應(yīng)用需要這個?!?
PQsocket 獲取用于后端聯(lián)接套接字的文件描述符號.一個有效的描述符應(yīng)該是 >= 0;一個 -1 表明當(dāng)前沒有打開與后端的聯(lián)接. 

int PQsocket(const PGconn *conn);
PQsocket 應(yīng)該用于獲取準(zhǔn)備調(diào)用 select(2) 的后端套接字描述符.這就允許一個應(yīng)用使用阻塞的聯(lián)接等待后端的響應(yīng)或者其他條件.如果 select(2) 的結(jié)果表明可以從后端套接字讀取數(shù)據(jù),那么應(yīng)該調(diào)用PQconsumeInput 讀取數(shù)據(jù);之后,PQisBusy,PQgetResult,和/或 PQnotifies 可用于處理返回信息. 
非阻塞的聯(lián)接(那些使用了 PQsetnonblocking 的聯(lián)接)在 PQflush 返回 0 之前,這表明沒有數(shù)據(jù)緩沖著等待發(fā)送給后端,不應(yīng)該使用 select。

一個使用這些函數(shù)的典型的前端將有一個主循環(huán)使用 select(2) 等待所有它必須處理的條件.其中一個條件將會是后端來的數(shù)據(jù)已準(zhǔn)備好,從 select 的角度來看就是 PQsocket 標(biāo)識的文件描述符上已經(jīng)有可讀取的數(shù)據(jù).當(dāng)主循環(huán)偵測到輸入準(zhǔn)備好,它將調(diào)用 PQconsumeInput 讀取輸入.然后可以調(diào)用 PQisBusy,如果 PQisBusy 返回 false(0)后面可以跟著 PQgetResult。同樣它(用戶應(yīng)用)可以調(diào)用 PQnotifies 測 NOTIFY 信息(參閱下面的 "異步通知").例子程序節(jié)里面給出了一個例子程序. 
一個使用 PQsendQuery/PQgetResult 的前端同樣也可以試圖取消一個正在被后端處理的查詢. 
  

PQrequestCancel 要求 Postgres 放棄處理當(dāng)前查詢. 
int PQrequestCancel(PGconn *conn);
如果取消的請求成功發(fā)送,返回值是 1,否則是 0.(如果為假,PQerrorMessage 會告之為什么.)不過,取消請求的成功發(fā)送將不保證請求將產(chǎn)生作用.不管 PQrequestCancel 的返回值是什么,應(yīng)用都必須繼續(xù)使用 PQgetResult進(jìn)行通常的后續(xù)的結(jié)果讀取工作.如果取消動作生效,當(dāng)前的查詢將提前退出并返回一個錯誤結(jié)果.如果取消動作失?。ㄒ簿褪呛蠖艘呀?jīng)處理完查詢了),那么將沒有可見的結(jié)果.
注意:如果當(dāng)前的查詢是事務(wù)的一部分,取消動作將退出整個事務(wù). 
PQrequestCancel 可以很好地從一個信號句柄里調(diào)用.所以,如果取消動作可以從信號句柄里發(fā)出的話,它也可以與簡單的 PQexec 一起使用.例如,psql 從一個 SIGINT 信號句柄里調(diào)用 PQrequestCancel,因此允許交互地取消通過 PQexec 運(yùn)行的查詢.注意,如果沒有與后端建立聯(lián)接或者后端當(dāng)前沒有處理查詢, PQrequestCancel將不發(fā)生做用.


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

查詢執(zhí)行函數(shù)
一旦與數(shù)據(jù)庫服務(wù)器的聯(lián)接成功建立,便可用這里描述的函數(shù)執(zhí)行 SQL 查詢和命令?!?
PQexec 提交一個查詢給 Postgres 并且等待結(jié)果. 
PGresult *PQexec(PGconn *conn,
                 const char *query);
返回一個 PGresult 指針或者也可能是一個 NULL 指針.通常返回一個非空(non-NULL)的指針,除非沒有內(nèi)存或發(fā)生了象不能把查詢發(fā)送到后端這樣的嚴(yán)重錯誤.如果返回的是 NULL,它應(yīng)該被當(dāng)作 PGRES_FATAL_ERROR 結(jié)果處理.用 PQerrorMessage 獲取有關(guān)錯誤的更多信息.
PGresult 結(jié)構(gòu)封裝了后端返回的查詢結(jié)果.libpq 應(yīng)用程序員應(yīng)該仔細(xì)維護(hù) PGresult.用下面的訪問函數(shù)來獲取 PGresult 的內(nèi)容.避免直接引用 PGresult 結(jié)構(gòu)的數(shù)據(jù)域,因為這個結(jié)構(gòu)可能會在未來被改變.(從 Postgres 版本 6.4 開始,結(jié)構(gòu) PGresult 的定義甚至都沒有放在 libpq-fe.h 里.如果你有一些直接訪問 PGresult 數(shù)據(jù)域的老代碼,你可以通過包含 libpq-int.h 繼續(xù)使用它們,但是我們鼓勵你立刻修改代碼.) 
PQresultStatus 返回查詢的結(jié)果狀態(tài). 
ExecStatusType PQresultStatus(const PGresult *res)
PQresultStatus 可以返回下面數(shù)值之一: 
PGRES_EMPTY_QUERY -- 發(fā)送給后端的字串是空的 
PGRES_COMMAND_OK -- 成功完成一個沒有返回數(shù)據(jù)的命令 

PGRES_TUPLES_OK -- 成功執(zhí)行查詢 
 

PGRES_COPY_OUT --?。◤姆?wù)器)Copy Out?。截惓觯?shù)據(jù)傳輸開始 
PGRES_COPY_IN -- Copy In?。截惾耄ǖ椒?wù)器)數(shù)據(jù)傳輸開始 

PGRES_BAD_RESPONSE -- 服務(wù)器的響應(yīng)無法理解 

PGRES_NONFATAL_ERROR 

PGRES_FATAL_ERROR

如果結(jié)果狀態(tài)是 PGRES_TUPLES_OK,那么下面的過程可以用于從查詢的返回中抽取記錄信息.注意一個碰巧檢索了零條記錄的 SELECT 仍然顯示 PGRES_TUPLES_OK。PGRES_COMMAND_OK 用于不返回記錄的命令(INSERT,UPDATE,等)。返回 PGRES_EMPTY_QUERY 的響應(yīng)通常意味著客戶端軟件里面的臭蟲?!?
PQresStatus 把 PQresultStatus 返回的枚舉類型轉(zhuǎn)換成一個描述狀態(tài)碼的字符串常量?!?

char *PQresStatus(ExecStatusType status);
PQresultErrorMessage 返回與查詢關(guān)聯(lián)的錯誤信息,或在沒有錯誤時返回一個空字符串. 
char *PQresultErrorMessage(const PGresult *res);
緊跟在一個 PQexec 或 PQgetResult 調(diào)用后面,PQerrorMessage?。▽β?lián)接)將返回與 PQresultErrorMessage (對結(jié)果)一樣的字符串.不過,一個 PGresult 將保有其錯誤信息直到被刪除,而連結(jié)的錯誤信息將在后續(xù)的操作完成時被改變.當(dāng)你想知道與某個 PGresult 相關(guān)聯(lián)的狀態(tài)時用 PQresultErrorMessage;當(dāng)你想知道與聯(lián)接的最近一個操作相關(guān)聯(lián)的狀態(tài)時用 PQerrorMessage; 
PQntuples 返回查詢結(jié)果里的記錄(實例)個數(shù). 

int PQntuples(const PGresult *res);
PQnfields 返回查詢結(jié)果里每個記錄的數(shù)據(jù)域(字段)的個數(shù). 
int PQnfields(const PGresult *res);
PQbinaryTuples 如果 PGresult 包含二進(jìn)制記錄數(shù)據(jù)時返回 1,如果包含 ASCII 數(shù)據(jù)返回 0. 
int PQbinaryTuples(const PGresult *res);
目前,二進(jìn)制記錄數(shù)據(jù)只能從一個從 BINARY 游標(biāo)里抽取數(shù)據(jù)的查詢返回. 
PQfname 返回與給出的數(shù)據(jù)域索引相關(guān)聯(lián)的數(shù)據(jù)域(字段)的名稱.?dāng)?shù)據(jù)域索引從 0 開始 

char *PQfname(const PGresult *res,
                    int field_index);
PQfnumber Returns the field (attribute) index associated with the given field name. 
int PQfnumber(const PGresult *res,
              const char *field_name);
-1 is returned if the given name does not match any field. 
PQftype 返回與給定數(shù)據(jù)域索引關(guān)聯(lián)的數(shù)據(jù)域類型.整數(shù)返回值是一個該類型的內(nèi)部編碼.?dāng)?shù)據(jù)域索引從 0 開始. 

Oid PQftype(const PGresult *res,
            int field_num);
你可以查詢系統(tǒng)表 pg_type 以獲取各種數(shù)據(jù)類型的名稱和屬性。內(nèi)建的數(shù)據(jù)類型的OID 在源碼樹的 src/include/catalog/pg_type.h 文件里定義?!?
PQfsize 返回與給定數(shù)據(jù)域索引關(guān)聯(lián)的數(shù)據(jù)域的大?。?dāng)?shù)據(jù)域索引從 0 開始. 

int PQfsize(const PGresult *res,
            int field_index);
PQfsize 返回在數(shù)據(jù)庫記錄里面給該數(shù)據(jù)域分配的空間,換句話說就是該數(shù)據(jù)類型在服務(wù)器里的二進(jìn)制形式的大?。ǔ叽纾绻摂?shù)據(jù)域是可變尺寸,返回 -1. 
PQfmod 返回與給定數(shù)據(jù)域索引相關(guān)聯(lián)的類型相關(guān)的修正數(shù)據(jù)(??).?dāng)?shù)據(jù)域索引從 0 開始. 

int PQfmod(const PGresult *res,
           int field_index);
PQgetvalue 返回一個 PGresult 里面的一條記錄的單獨(dú)的一個數(shù)據(jù)域(字段)的值.記錄和數(shù)據(jù)域索引從 0 開始. 
char* PQgetvalue(const PGresult *res,
                 int tup_num,
                 int field_num);
對大多數(shù)查詢而言,PQgetvalue 返回的值是一個表示字段值的空(NULL)結(jié)尾的ASCII 字符串.但是如果 PQbinaryTuples() 為 1,PQgetvalue 返回的值就是該類型在后端服務(wù)器內(nèi)部的二進(jìn)制表現(xiàn)形式(但是不包括尺寸字--如果數(shù)據(jù)域是變長的).這樣,把數(shù)據(jù)轉(zhuǎn)換成對應(yīng)的 C 類型就是程序員的責(zé)任了.PQgetvalue 返回的指針指向一個本身是 PGresult 結(jié)構(gòu)的一部分的存儲區(qū)域.我們不能更改它,并且如果我們要在 PGresult 結(jié)構(gòu)的生存期后還要使用它的話,我們必須顯式的把該數(shù)值拷貝到其他存儲器中. 
PQgetlength 返回以字節(jié)計的數(shù)據(jù)域(字段)的長度.記錄和數(shù)據(jù)域索引從 0 開始. 

int PQgetlength(const PGresult *res,
                int tup_num,
                int field_num);
這是某一特定數(shù)據(jù)值的實際數(shù)據(jù)長度,也就是由 PQgetvalue 指向的對象的尺寸.注意,對于 ASCII 代表的數(shù)值,這個尺寸與 PQfsize 報告的二進(jìn)制尺寸無關(guān). 
PQgetisnull 測試一個數(shù)據(jù)域是否為空(NULL).記錄和數(shù)據(jù)域索引從 0 開始. 

int PQgetisnull(const PGresult *res,
                int tup_num,
                int field_num);
如果該域包含 NULL,函數(shù)返回 1,如果包含非空(non-null?。┲担祷亍?.(注意,對一個 NULL 數(shù)據(jù)域,PQgetvalue 將返回一個空字符串,不是一個空指針.) 
PQcmdStatus 返回產(chǎn)生 PGresult 的 SQL 命令的命令狀態(tài)字符串. 

char * PQcmdStatus(const PGresult *res);
PQcmdTuples 返回被 SQL 命令影響的行的數(shù)量. 
char * PQcmdTuples(const PGresult *res);
如果產(chǎn)生 PGresult 的 SQL 命令是 INSERT,UPDATE 或 DELETE,這里返回涉及行的行數(shù).如果是其他命令返回一個空字符串. 
PQoidValue 返回一個插入的記錄的記錄對象標(biāo)識(OID)――如果SQL 命令是 INSERT.否則,返回 InvalidOid?。?

Oid PQoidValue(const PGresult *res);
如果你包含了 libpq 頭文件,那么 Oid 和常量 InvalidOid 的類型將被定義。他們都是某種整型類型?!?
PQoidStatus 返回一個被插入的記錄的對象標(biāo)識的字串,如果SQL 命令是 INSERT。否則. 返回一個空字串?!?

char * PQoidStatus(const PGresult *res);
因為有了 PQoidValue,我們不建議使用這個函數(shù),而且它在線程里使用也是不安全的。
    PQprint 向指定的輸出流打印所有的記錄和(可選的)字段名稱. 
void PQprint(FILE* fout,      /* output stream */
             const PGresult *res,
             const PQprintOpt *po);

struct {
    pqbool  header;      /* print output field headings and row count */
    pqbool  align;       /* fill align the fields */
    pqbool  standard;    /* old brain dead format */
    pqbool  html3;       /* output html tables */
    pqbool  expanded;    /* expand tables */
    pqbool  pager;       /* use pager for output if needed */
    char    *fieldSep;   /* field separator */
    char    *tableOpt;   /* insert to HTML table ... */
    char    *caption;    /* HTML caption */
    char    **fieldName; /* null terminated array of replacement field names */
} PQprintOpt;
這個函數(shù)以前被 psql 用于打印查詢結(jié)果,但是現(xiàn)在已經(jīng)不用這個函數(shù)了,并且此函數(shù)不再有活躍的支持。 
PQclear 釋放與 PGresult 關(guān)聯(lián)的存儲器.當(dāng)不再需要時,每個查詢結(jié)果都應(yīng)該通過 PQclear 釋放. 

void PQclear(PQresult *res);
你可以保留 PGresult 對象任意長的時間;當(dāng)你提交新的查詢時它并不消失,甚至你斷開聯(lián)接后也是這樣.要刪除它,你必須調(diào)用 PQclear.不這么做將導(dǎo)致前端的存儲器泄漏. 
PQmakeEmptyPGresult 構(gòu)建一個給出狀態(tài)的空的 PGresult 對象. 

PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
這是 libpq 的內(nèi)部過程,用于分配和初始化一個空 PGresult 對象.它被輸出是因為一些應(yīng)用需要自行生成結(jié)果對象(尤其是特定的帶有錯誤狀態(tài)的對象).如果 conn 非空(NULL)并且狀態(tài)指示一個錯誤,聯(lián)接當(dāng)前的 errorMessage 被拷貝到 PGresult.注意最終對該對象要調(diào)用 PQclear,正如 libpq 本身返回的 PGresult 一樣.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

捷徑
PostgreSQL 提供一個發(fā)往后端的函數(shù)調(diào)用的捷徑接口.這是一個通向系統(tǒng)內(nèi)部的后門,因而可能存在安全漏洞.大多數(shù)用戶應(yīng)該不需要這個特性. 
PQfn 通過捷徑接口執(zhí)行請求的后端函數(shù). 
PGresult* PQfn(PGconn* conn,
               int fnid,
               int *result_buf,
               int *result_len,
               int result_is_int,
               const PQArgBlock *args,
               int nargs);
fnid 參數(shù)是待執(zhí)行的函數(shù)的對象標(biāo)識(OID).result_buf 是放置返回值的緩沖區(qū).調(diào)用者必須為返回值分配足夠的空間(這里沒有檢查!).實際的返回值長度將被放在 result_len 指向的整數(shù)里返回.如果預(yù)期返回值是 4-字節(jié)整數(shù),把 result_is_int 設(shè)為 1;否則設(shè)為 0.(把 result_is_int 設(shè)為 1 告訴 libpq 必要時交換數(shù)值的字節(jié)序,這樣就可以正確地傳輸成客戶機(jī)上的整數(shù)值.當(dāng)result_is_int 是 0 時,后端發(fā)送回來的字節(jié)串不做修改.)args 和 nargs 聲明要傳入函數(shù)的參數(shù). 
typedef struct {
    int len;
    int isint;
    union {
        int *ptr;
        int integer;
    } u;
} PQArgBlock;
PQfn 總是返回一個有效的 PGresult*.在使用結(jié)果之前應(yīng)該檢查 resultStatus.當(dāng)結(jié)果不再使用后,調(diào)用者有責(zé)任使用 PQclear 釋放 PGresult.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

異步通知
PostgreSQL 支持通過 LISTEN 和 NOTIFY 命令產(chǎn)生的異步通知.一個后端用 LISTEN 命令注冊一個它感興趣的通知條件(也可以用 UNLISTEN 去掉這個注冊).所有正在監(jiān)聽某一通知條件的后端在該條件名的 NOTIFY?。ㄍㄖ┍蝗魏魏蠖藞?zhí)行后都將被異步地通知.通知發(fā)出者不會傳遞附加的信息從到通知接收者.因此,很典型地是,任何實際的需要被傳遞的數(shù)據(jù)都是通過一個數(shù)據(jù)庫關(guān)系傳遞的.通常,條件名與相關(guān)聯(lián)的關(guān)系同名,但是并不是一定要與某個關(guān)系相關(guān)才行. 
libpq 應(yīng)用把 LISTEN 和 UNLISTEN 命令作為通常的 SQL 查詢提交.因此,通過調(diào)用 PQnotifies() 可以偵測到 NOTIFY 消息的到達(dá). 

PQnotifies 從一個來自后端的未處理的通知信息列表中返回下一條通知.如果沒有未處理的信息則返回 NULL.一旦 PQnotifies 返回一條通知,該通知會被認(rèn)為已處理并且將被從通知列表中刪除. 
PGnotify* PQnotifies(PGconn *conn);

typedef struct pgNotify {
    char relname[NAMEDATALEN];       /* name of relation
                                      * containing data */
    int  be_pid;                     /* process id of backend */
} PGnotify;
在處理完 PQnotifies 返回的 PGnotify 對象后,別忘了用 free() 把它釋放,以避免內(nèi)存泄漏?!?
注意:在 PostgreSQL 6.4 和更高的版本里,be_pid 是正在通知的后端的 PID,而在早些的版本里它總是你自己的后端的PID。
第二個樣本程序給出一個使用異步通知的例子. 
PQnotifies() 實際上不讀取后端數(shù)據(jù);它只是返回被前面的另一個libpq 函數(shù)吸收的信息.在以前的 libpq 的版本里,周期性的收到通知(NOTIFY)信息的唯一方法是持續(xù)的提交查詢,即使是空查詢也可以,并且在每次 PQexec() 后檢查 PQnotifies()?,F(xiàn)在這個方法也能還工作,不過我們認(rèn)為它太浪費(fèi)處理器時間而廢棄了它?!?

在你沒有可用的查詢提交時檢查 NOTIFY 消息的更好的方法是調(diào)用 PQconsumeInput(),然后檢查 PQnotifies().你可以使用 select(2) 來等待后端數(shù)據(jù)的到達(dá),這樣在沒有數(shù)據(jù)可處理時可以不浪費(fèi)CPU 時間.注意這種方法不管你使用 PQsendQuery/ PQgetResult 還是簡單的 PQexec 來執(zhí)行查詢都能工作.不過,你應(yīng)該記住在每次 PQgetResult 或 PQexec 后檢查 PQnotifies(),看看在處理查詢的過程中是否有通知到達(dá).


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

與 COPY 命令相關(guān)的函數(shù)
PostgreSQL 里的 COPY 命令里有用于 libpq 里從網(wǎng)絡(luò)聯(lián)接讀出或者寫入的選項.因此,這些函數(shù)有必要直接訪問網(wǎng)絡(luò)聯(lián)接,以便應(yīng)用可以充分利用這個功能. 
這些函數(shù)應(yīng)該只在從 PQexec 或 PQgetResult 獲得了 PGRES_COPY_OUT 或 PGRES_COPY_IN 結(jié)果對象的情況下執(zhí)行. 

PQgetline 讀取一個以回車符(換行符)結(jié)尾的字符行中指定字節(jié)數(shù)的字符(由后端服務(wù)器傳輸)到一個字符串緩沖區(qū). 
int PQgetline(PGconn *conn,
              char *string,
              int length)
類似 fgets(3),這個過程拷貝最多 length-1 個字符到字符串里.但是它會象 gets(3) 那樣把結(jié)尾的換行符轉(zhuǎn)換成一個空字符(null).PQgetline 在碰到 EOF 時返回 EOF,如果整行都被讀取了返回 0,如果緩沖區(qū)填滿了而還沒有遇到結(jié)束的換行符則返回 1. 
注意,應(yīng)用程序必須檢查新行是否包含兩個字符 "/.",這表明后端服務(wù)器已經(jīng)完成了 copy 命令的結(jié)果集的發(fā)送.如果應(yīng)用可能收到超過 length-1 字符長的字符,我們就應(yīng)該確保正確識別"/."行(例如,不要把一個長的行的結(jié)束當(dāng)作一個終止行).src/bin/psql/copy.c 里的代碼包含正確控制 copy 協(xié)議的過程. 

PQgetlineAsync 不做阻塞地讀取一行以換行符結(jié)尾的字符(由后端服務(wù)器傳輸)到一個緩沖區(qū)中. 

int PQgetlineAsync(PGconn *conn,
                   char *buffer,
                   int bufsize)
這個過程類似于 PQgetline,但是可以用于那些必須異步地讀取 COPY 數(shù)據(jù)的應(yīng)用,因而是不阻塞的.在使用了 COPY 命令和獲取了 PGRES_COPY_OUT 響應(yīng)之后,應(yīng)用應(yīng)該調(diào)用 PQconsumeInput 和 PQgetlineAsync 直到收到數(shù)據(jù)結(jié)束的信號.不象 PQgetline,這個過程負(fù)責(zé)檢測數(shù)據(jù)結(jié)束.在每次調(diào)用時,如果 libpq 的輸入緩沖區(qū)內(nèi)有可用的一個完整的換行符結(jié)尾的數(shù)據(jù)行或者調(diào)用者聲明的緩沖區(qū)小于到來的數(shù)據(jù)行的長度,PQgetlineAsync 都將返回數(shù)據(jù).否則,在其他數(shù)據(jù)到達(dá)之前不會返回數(shù)據(jù). 
如果見到了拷貝數(shù)據(jù)結(jié)束的信號,此過程返回 -1,如果沒有可用數(shù)據(jù)返回 0,或者是給出一個正數(shù)表明返回的數(shù)據(jù)的字節(jié)數(shù).如果返回 -1,調(diào)用者下一步必須調(diào)用 PQendcopy,然后回到正常處理.返回的數(shù)據(jù)將不會超出換行符的范圍.如果可能,每次將返回一個完整行.但如果調(diào)用者提供的緩沖區(qū)太小,無法容下后端發(fā)出的整行,那么將返回部分行.這個可以通過測試返回的最后一個字節(jié)是否“/n” 來確認(rèn).返回的字符串不是空結(jié)尾的.(如果你想得到一個空結(jié)尾的字串,確保你傳遞了一個比實際大小少一字節(jié)的緩沖區(qū).) 

PQputline 向后端服務(wù)器發(fā)送一個空結(jié)尾的字符串.成功時返回 0,如果不能發(fā)送字符串返回 EOF. 

int PQputline(PGconn *conn,
              const char *string);
注意,應(yīng)用在最后一行時必須顯式的發(fā)送兩個字符 "/.",通知后端它已經(jīng)完成數(shù)據(jù)發(fā)送. 
PQputnbytes 向后端服務(wù)器發(fā)送一個非空結(jié)尾的字符串.成功時返回 0,如果不能發(fā)送字符串返回 EOF. 

int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);
此函數(shù)類似 PQputline,除了數(shù)據(jù)緩沖區(qū)不需要是空結(jié)尾的,因為要發(fā)送的字節(jié)數(shù)是直接聲明的. 
PQendcopy 與后端同步.這個函數(shù)等到后端完成拷貝(才返回?).你可以在用 PQputline 向后端發(fā)送完最后一個字符串后或者用 PGgetline 從后端獲取最后一行字符串后調(diào)用它.我們必須調(diào)用這個函數(shù),否則后端可能會和前端“同步丟失”。在這個函數(shù)返回后,后端就已經(jīng)準(zhǔn)備好接收下一個查詢了.成功時返回 0,否則返回非零值. 

int PQendcopy(PGconn *conn);
一個例子: 
PQexec(conn, "create table foo (a int4, b char(16), d float8)");
PQexec(conn, "copy foo from stdin");
PQputline(conn, "3/thello world/t4.5/n");
PQputline(conn,"4/tgoodbye world/t7.11/n");
...
PQputline(conn,"http://./n");
PQendcopy(conn);
在使用 PQgetResult 時,應(yīng)用應(yīng)該對 PGRES_COPY_OUT 的結(jié)果做出反應(yīng):重復(fù)調(diào)用 PQgetline,并且在收到結(jié)束行時調(diào)用 PQendcopy.然后應(yīng)該返回到 PQgetResult 循環(huán)直到 PQgetResult 返回 NULL.類似的 PGRES_COPY_IN 結(jié)果是用一系列 PQputline 調(diào)用最后跟著 PQendcopy,然后返回到 PQgetResult 循環(huán).這樣的排列將保證嵌入到一系列SQL 命令里的 copy in 或 copy out 命令將被正確執(zhí)行. 
舊的應(yīng)用大多通過 PQexec 提交一個 copy in 或 copy out 命令并且假設(shè)在 PQendcopy 后事務(wù)完成.這樣只有在 copy in/out 是查詢字符串里的唯一的 SQL 命令才能正確工作.


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

libpq 跟蹤函數(shù)
PQtrace 打開對前端/后端通訊的跟蹤,把調(diào)試信息輸出到一個文件流里. 
void PQtrace(PGconn *conn
             FILE *debug_port)
PQuntrace 關(guān)閉 PQtrace 打開的跟蹤 
void PQuntrace(PGconn *conn)

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

libpq 控制函數(shù)
PQsetNoticeProcessor 控制 libpq 生成的通知和警告信息的匯報. 
typedef void (*PQnoticeProcessor) (void *arg, const char *message);

PQnoticeProcessor
PQsetNoticeProcessor(PGconn *conn,
                     PQnoticeProcessor proc,
                     void *arg);
缺省時, libpq 在后端往 stderr 上打印“通知”信息和一些它自身生成的錯誤信息.這個特性可以通過提供一個對信息進(jìn)行某種加工的回叫函數(shù)來更改.我們向這個回叫函數(shù)傳遞錯誤信息的文本(包括文本結(jié)尾的換行符),和一個空(void)指針,該指針與傳遞給 PQsetNoticeProcessor 的完全一樣.(如果需要,這個指針可以用于訪問應(yīng)用相關(guān)的狀態(tài).)缺省的通知處理器只是簡單的 
static void
defaultNoticeProcessor(void * arg, const char * message)
{
    fprintf(stderr, "%s", message);
}
要使用特殊的通知處理器,在創(chuàng)建完新的 PGconn 對象后馬上調(diào)用 PQsetNoticeProcessor?!?
返回值是指向以前的通知處理器的指針。如果你提供了一個 NULL 做為回調(diào)指針,那么不會發(fā)生任何動作,只是返回當(dāng)前的指針?!?

一旦你設(shè)置了一個通知處理器,你就應(yīng)該預(yù)料到該函數(shù)可能在 PGconn 對象或 PGresult 對象從開始存在時起就可能被調(diào)用.當(dāng)創(chuàng)建一個 PGresult 時,PGconn 的當(dāng)前的通知指針被拷貝到 PGresult 里提供給可能需要的過程,如 PQgetvalue 使用.


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

環(huán)境變量
下面的環(huán)境變量可以用于選擇缺省的聯(lián)接參數(shù)值,這些值將被 PQconnectdb 或 PQsetdbLogin 使用--如果調(diào)用代碼沒有直接聲明相應(yīng)值的話.這些(環(huán)境變量)可以避免把麻煩的數(shù)據(jù)庫名強(qiáng)加入簡單的應(yīng)用程序的硬代碼里面.The following environment variables can be used to select default connection parameter values, which will be used by PQconnectdb or PQsetdbLogin if no value is directly specified by the calling code. These are useful to avoid hard-coding database names into simple application programs. 
PGHOST 設(shè)置缺省的服務(wù)器名.如果聲明了一個非零長的字符串,將使用 TCP/IP 通訊.如果沒有主機(jī)名,libpq 將使用本地的Unix 域套接字. 
PGPORT 設(shè)置與 Postgres 后端通訊的缺省端口號或本地 Unix 主控套接字的文件擴(kuò)展(文件標(biāo)識符). 

PGDATABASE 設(shè)置缺省的 PostgreSQL 數(shù)據(jù)庫名. 

PGUSER 設(shè)置用于與數(shù)據(jù)庫聯(lián)接和用于認(rèn)證的用戶名. 

PGPASSWORD 如果后端要求口令認(rèn)證,設(shè)置使用的口令. 

PGREALM 設(shè)置與 Postgres 一起使用的 Kerberos?。绻撚蚺c本地域不同的話。如果設(shè)置了 PGREALM,Postgres 應(yīng)用將試圖用這個域(realm)與服務(wù)器進(jìn)行認(rèn)證并且使用獨(dú)立的門票文件(ticket files)以避免與本地的門票文件沖突.只有在后端選擇了 Kerberos 認(rèn)證時才使用這個環(huán)境變量.(譯注:門票文件是 Kerberos 認(rèn)證協(xié)議中用于交換密鑰的一個文件/服務(wù)器。) 

PGOPTIONS 為 Postgres 設(shè)置附加的運(yùn)行時選項. 

PGTTY 設(shè)置后端調(diào)試信息顯示輸出的文件或者控制臺(tty).

下面的環(huán)境變量可以用于為每個 Postgres 會話聲明用戶級別的缺省特性: 
PGDATESTYLE 設(shè)置缺省的日期/時間表現(xiàn)形式. 
PGTZ 設(shè)置缺省的時區(qū). 

PGCLIENTENCODING 設(shè)置缺省的客戶端編碼(如果配制 Postgres 時選擇了 MULTIBYTE 支持).

下面的環(huán)境變量可以用于為每個 Postgres 會話聲明缺省的內(nèi)部特性: 
PGGEQO 為基因優(yōu)化器設(shè)置缺省模式.
參閱 SET SQL 命令獲取這些環(huán)境變量的正確值的信息. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

線程特性
到 Postgres 7.0 時 libpq 是線程安全的,只要不是兩個線程試圖同時操作同一個 PGconn 對象.實際上,你無法從不同的線程向同一個聯(lián)接對象發(fā)出并發(fā)的查詢.(如果你需要運(yùn)行并行查詢,請啟動多個聯(lián)接.) 
PGresult 對象在創(chuàng)建后是只讀的,因此可以自由地在線程之間傳遞. 

過時了的函數(shù) PQoidStatus 和 fe_setauthsvc 都是線程不安全的,因此不應(yīng)該在一個多線程的程序里面使用.PQoidStatus 可以由 PQoidValue代替.而我們覺得根本沒有調(diào)用 fe_setauthsvc 的必要.


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

例子程序
例子程序 1
/*
 * testlibpq.c Test the C version of Libpq, the Postgres frontend
 * library.
 *
 *
 */
#include 
#include "libpq-fe.h"

void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

main()
{
    char       *pghost,
               *pgport,
               *pgoptions,
               *pgtty;
    char       *dbName;
    int         nFields;
    int         i,
                j;

    /* FILE *debug; */

    PGconn     *conn;
    PGresult   *res;

    /*
     * begin, by setting the parameters for a backend connection if the
     * parameters are null, then the system will try to use reasonable
     * defaults by looking up environment variables or, failing that,
     * using hardwired constants
     */
    pghost = NULL;              /* host name of the backend server */
    pgport = NULL;              /* port of the backend server */
    pgoptions = NULL;           /* special options to start up the backend
                                 * server */
    pgtty = NULL;               /* debugging tty for the backend server */
    dbName = "template1";

    /* make a connection to the database */
    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

    /*
     * check to see that the backend connection was successfully made
     */
    if (PQstatus(conn) == CONNECTION_BAD)
    {
        fprintf(stderr, "Connection to database '%s' failed./n", dbName);
        fprintf(stderr, "%s", PQerrorMessage(conn));
        exit_nicely(conn);
    }

    /* debug = fopen("/tmp/trace.out","w"); */
    /* PQtrace(conn, debug);  */

    /* start a transaction block */
    res = PQexec(conn, "BEGIN");
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "BEGIN command failed/n");
        PQclear(res);
        exit_nicely(conn);
    }

    /*
     * should PQclear PGresult whenever it is no longer needed to avoid
     * memory leaks
     */
    PQclear(res);

    /*
     * fetch instances from the pg_database, the system catalog of
     * databases
     */
    res = PQexec(conn, "DECLARE mycursor CURSOR FOR select * from pg_database");
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "DECLARE CURSOR command failed/n");
        PQclear(res);
        exit_nicely(conn);
    }
    PQclear(res);
    res = PQexec(conn, "FETCH ALL in mycursor");
    if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "FETCH ALL command didn't return tuples properly/n");
        PQclear(res);
        exit_nicely(conn);
    }

    /* first, print out the attribute names */
    nFields = PQnfields(res);
    for (i = 0; i < nFields; i++)
        printf("%-15s", PQfname(res, i));
    printf("/n/n");

    /* next, print out the instances */
    for (i = 0; i < PQntuples(res); i++)
    {
        for (j = 0; j < nFields; j++)
            printf("%-15s", PQgetvalue(res, i, j));
        printf("/n");
    }
    PQclear(res);

    /* close the cursor */
    res = PQexec(conn, "CLOSE mycursor");
    PQclear(res);

    /* commit the transaction */
    res = PQexec(conn, "COMMIT");
    PQclear(res);

    /* close the connection to the database and cleanup */
    PQfinish(conn);

    /* fclose(debug); */
    return 0;

}
例子程序 2
/*
 * testlibpq2.c
 *  Test of the asynchronous notification interface
 *
 * Start this program, then from psql in another window do
 *   NOTIFY TBL2;
 *
 * Or, if you want to get fancy, try this:
 * Populate a database with the following:
 *
 *   CREATE TABLE TBL1 (i int4);
 *
 *   CREATE TABLE TBL2 (i int4);
 *
 *   CREATE RULE r1 AS ON INSERT TO TBL1 DO
 *     (INSERT INTO TBL2 values (new.i); NOTIFY TBL2);
 *
 * and do
 *
 *   INSERT INTO TBL1 values (10);
 *
 */
#include 
#include "libpq-fe.h"

void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

main()
{
    char       *pghost,
               *pgport,
               *pgoptions,
               *pgtty;
    char       *dbName;
    int         nFields;
    int         i,
                j;

    PGconn     *conn;
    PGresult   *res;
    PGnotify   *notify;

    /*
     * begin, by setting the parameters for a backend connection if the
     * parameters are null, then the system will try to use reasonable
     * defaults by looking up environment variables or, failing that,
     * using hardwired constants
     */
    pghost = NULL;              /* host name of the backend server */
    pgport = NULL;              /* port of the backend server */
    pgoptions = NULL;           /* special options to start up the backend
                                 * server */
    pgtty = NULL;               /* debugging tty for the backend server */
    dbName = getenv("USER");    /* change this to the name of your test
                                 * database */

    /* make a connection to the database */
    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

    /*
     * check to see that the backend connection was successfully made
     */
    if (PQstatus(conn) == CONNECTION_BAD)
    {
        fprintf(stderr, "Connection to database '%s' failed./n", dbName);
        fprintf(stderr, "%s", PQerrorMessage(conn));
        exit_nicely(conn);
    }

    res = PQexec(conn, "LISTEN TBL2");
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "LISTEN command failed/n");
        PQclear(res);
        exit_nicely(conn);
    }

    /*
     * should PQclear PGresult whenever it is no longer needed to avoid
     * memory leaks
     */
    PQclear(res);

    while (1)
    {

        /*
         * wait a little bit between checks; waiting with select()
         * would be more efficient.
         */
        sleep(1);
        /* collect any asynchronous backend messages */
        PQconsumeInput(conn);
        /* check for asynchronous notify messages */
        while ((notify = PQnotifies(conn)) != NULL)
        {
            fprintf(stderr,
                 "ASYNC NOTIFY of '%s' from backend pid '%d' received/n",
                    notify->relname, notify->be_pid);
            free(notify);
        }
    }

    /* close the connection to the database and cleanup */
    PQfinish(conn);

    return 0;
}
例子程序 3
/*
 * testlibpq3.c Test the C version of Libpq, the Postgres frontend
 * library. tests the binary cursor interface
 *
 *
 *
 * populate a database by doing the following:
 *
 * CREATE TABLE test1 (i int4, d float4, p polygon);
 *
 * INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0,
 * 2.0)'::polygon);
 *
 * INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0,
 * 1.0)'::polygon);
 *
 * the expected output is:
 *
 * tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4
 * bytes) 2 points   boundbox = (hi=3.000000/4.000000, lo =
 * 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes)
 * 89.050003, p = (4 bytes) 2 points   boundbox =
 * (hi=4.000000/3.000000, lo = 2.000000,1.000000)
 *
 *
 */
#include 
#include "libpq-fe.h"
#include "utils/geo-decls.h"    /* for the POLYGON type */

void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

main()
{
    char       *pghost,
               *pgport,
               *pgoptions,
               *pgtty;
    char       *dbName;
    int         nFields;
    int         i,
                j;
    int         i_fnum,
                d_fnum,
                p_fnum;
    PGconn     *conn;
    PGresult   *res;

    /*
     * begin, by setting the parameters for a backend connection if the
     * parameters are null, then the system will try to use reasonable
     * defaults by looking up environment variables or, failing that,
     * using hardwired constants
     */
    pghost = NULL;              /* host name of the backend server */
    pgport = NULL;              /* port of the backend server */
    pgoptions = NULL;           /* special options to start up the backend
                                 * server */
    pgtty = NULL;               /* debugging tty for the backend server */

    dbName = getenv("USER");    /* change this to the name of your test
                                 * database */

    /* make a connection to the database */
    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

    /*
     * check to see that the backend connection was successfully made
     */
    if (PQstatus(conn) == CONNECTION_BAD)
    {
        fprintf(stderr, "Connection to database '%s' failed./n", dbName);
        fprintf(stderr, "%s", PQerrorMessage(conn));
        exit_nicely(conn);
    }

    /* start a transaction block */
    res = PQexec(conn, "BEGIN");
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "BEGIN command failed/n");
        PQclear(res);
        exit_nicely(conn);
    }

    /*
     * should PQclear PGresult whenever it is no longer needed to avoid
     * memory leaks
     */
    PQclear(res);

    /*
     * fetch instances from the pg_database, the system catalog of
     * databases
     */
    res = PQexec(conn, "DECLARE mycursor BINARY CURSOR FOR select * from test1");
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "DECLARE CURSOR command failed/n");
        PQclear(res);
        exit_nicely(conn);
    }
    PQclear(res);

    res = PQexec(conn, "FETCH ALL in mycursor");
    if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "FETCH ALL command didn't return tuples properly/n");
        PQclear(res);
        exit_nicely(conn);
    }

    i_fnum = PQfnumber(res, "i");
    d_fnum = PQfnumber(res, "d");
    p_fnum = PQfnumber(res, "p");

    for (i = 0; i < 3; i++)
    {
        printf("type[%d] = %d, size[%d] = %d/n",
               i, PQftype(res, i),
               i, PQfsize(res, i));
    }
    for (i = 0; i < PQntuples(res); i++)
    {
        int        *ival;
        float      *dval;
        int         plen;
        POLYGON    *pval;

        /* we hard-wire this to the 3 fields we know about */
        ival = (int *) PQgetvalue(res, i, i_fnum);
        dval = (float *) PQgetvalue(res, i, d_fnum);
        plen = PQgetlength(res, i, p_fnum);

        /*
         * plen doesn't include the length field so need to
         * increment by VARHDSZ
         */
        pval = (POLYGON *) malloc(plen + VARHDRSZ);
        pval->size = plen;
        memmove((char *) &pval->npts, PQgetvalue(res, i, p_fnum), plen);
        printf("tuple %d: got/n", i);
        printf(" i = (%d bytes) %d,/n",
               PQgetlength(res, i, i_fnum), *ival);
        printf(" d = (%d bytes) %f,/n",
               PQgetlength(res, i, d_fnum), *dval);
        printf(" p = (%d bytes) %d points /tboundbox = (hi=%f/%f, lo = %f,%f)/n",
               PQgetlength(res, i, d_fnum),
               pval->npts,
               pval->boundbox.xh,
               pval->boundbox.yh,
               pval->boundbox.xl,
               pval->boundbox.yl);
    }
    PQclear(res);

    /* close the cursor */
    res = PQexec(conn, "CLOSE mycursor");
    PQclear(res);

    /* commit the transaction */
    res = PQexec(conn, "COMMIT");
    PQclear(res);

    /* close the connection to the database and cleanup */
    PQfinish(conn);

    return 0;
}

--------------------------------------------------------------------------------
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表

圖片精選

在线天堂中文字幕| 日韩av不卡在线播放| 国产av一区二区三区| 99久re热视频精品98| av资源免费观看| 国产成人97精品免费看片| 亚洲精品久久久久| 亚洲av熟女高潮一区二区| 亚洲日本韩国一区| 亚洲 欧洲 日韩| 久久久精品福利| 一级特黄aaaaaa大片| 在线观看视频污| 精品亚洲男同gayvideo网站| 国产福利在线播放| 精品国产髙清在线看国产毛片| 17c精品麻豆一区二区免费| 成在在线免费视频| 国产剧情一区二区在线观看| 精品综合久久久| 全彩无遮拦全彩口工漫画全彩| 无码无遮挡又大又爽又黄的视频| 成人av电影免费在线播放| 先锋影音男人资源| 欧美性xxxxxxx| 国产小视频在线高清播放| 99久久久无码国产精品不卡| 爱爱爱爱免费视频| 精品国产一区二区三区久久久蜜臀| 免费人成视频在线| 亚洲综合网站| 欧美第一页在线| 男男受被啪到高潮自述| 日韩午夜视频在线观看| 91深夜福利| 久久人人99| 欧美一区二区久久久| 日本欧美加勒比视频| 99精品女人在线观看免费视频| 欧美三级华人主播| 国产亚洲福利| 日夜操在线视频| 久做在线视频免费观看| 日韩亚洲在线| 国产99在线播放| 国产一级精品毛片| caoporn97在线视频| 色屁屁一区二区| 全程偷拍露脸中年夫妇| 久久亚州av| 国产河南妇女毛片精品久久久| av日韩在线免费| 欧美丝袜一区二区三区| 久久国产精品成人免费观看的软件| 欧美一级欧美三级| 欧美xxxx在线观看| 国产精品理论在线观看| 国产人妻精品一区二区三区| 精品一区二区三区免费站| 在线观看免费网站| 日韩激情图片| 成人av综合在线| 国产一级片视频| 牲欧美videos精品| av大全在线| 51国偷自产一区二区三区| 少妇无码av无码专区在线观看| 成人精品在线视频观看| 6080yy午夜一二三区久久| 国产av精国产传媒| 日本免费黄色小视频| 五月婷婷视频在线观看| 国产欧美一区二区三区视频| 99九九99九九九99九他书对| 日本中文字幕网| 日本高清免费在线视频| 日韩伦理一区二区三区av在线| 中文字幕欧美日韩精品| 日本一二三视频| 久草视频观看| 欧美日韩一区二区免费视频| 欧美日韩精品在线观看| 最新在线观看av网站| 99热精品一区二区| 国产午夜一区二区三区| 国产美女网站在线观看| 亚洲丰满在线| 久久不见久久见国语| 人交獸av完整版在线观看| 91超碰碰碰碰久久久久久综合| 国产日韩精品视频一区| 久久亚洲国产精品一区二区| 久久美女高清视频| 欧美偷拍第一页| 丰满少妇xbxb毛片日本| 中文在线观看免费| 精品视频97| 这里视频有精品| 黄色成人小视频| baoyu777.永久免费视频| 在线观看成人毛片| 美女福利视频网| 免费在线观看视频a| 日本免费网址| av中文字幕一区二区三区| 亚洲av人人澡人人爽人人夜夜| 国产精品视频精品| 久久久综合视频| 亚洲超碰在线观看| 国产极品美女到高潮| 二级片在线观看| 丝袜制服影音先锋| 午夜av不卡| 黄色一级片黄色| 99国精产品一二二线| 狼人精品一区二区三区在线| 欧美欧美欧美欧美首页| 欧美网站大全在线观看| 91在线你懂的| 免费在线视频你懂的| 国产精品久在线观看| 亚洲 欧美 国产 另类| 777久久久精品| 国产精品视频第一区| 欧美成人高清在线| 欧美大片在线免费观看| 夜夜精品视频一区二区| 精品视频在线一区二区| 福利电影导航| 国产亚洲欧美aaaa| 国产日韩中文字幕| 亚洲另类图片另类电影| 国产精品va无码一区二区三区| 日韩精品av一区二区三区| 国产在线视频2019最新视频| 亚洲一区国产| 99久久激情视频| 国产亚洲精品久久久网站好莱| 欧洲视频一区二区三区| 中国一级片黄色一级片黄| 最近中文字幕在线视频| 国产精品一区二区羞羞答答| 欧美夫妻性生活xx| 亚洲综合区在线| 日韩在线你懂得| 天天色综合天天| 99精品视频在线播放免费| 亚洲精品99久久久久中文字幕| 国产精品亚洲色图| 国产欧美一区二区三区小说| 青青视频免费在线观看| 91精品国产色综合久久不卡粉嫩| 91精品成人| 婷婷精品在线观看| 夜鲁夜鲁夜鲁视频在线播放| a级黄色毛片| 69av在线视频| 九色视频在线观看免费| 国产三级精品视频| 亚洲福利免费| 六月婷婷在线视频| 鲁一鲁一鲁一鲁一av| 少妇的滋味中文字幕bd| 日韩精品一区二区三区外面| 国产日韩成人精品| 性欧美video视频另类| 婷婷丁香六月天| 国产精品免费一区二区三区观看| 亚洲成人自拍网| 久久亚洲精华国产精华液| 在线视频不卡一区二区三区| 日韩精品免费一区二区夜夜嗨| 人妻 日韩 欧美 综合 制服| 欧美精品一二三| 免费黄在线观看| 亚洲日本成人网| 婷婷五月综合激情| www.com污| 国产精品v欧美精品v日韩| 成人国产精品一区二区免费麻豆| 青娱乐国产盛宴| 国产精品对白久久久久粗| 黄色免费看网站| 中文字幕一区三区| 久久久久久91精品色婷婷| av在线之家电影网站| 美女被爆操网站| 亚洲精品视频久久久| 国产欧美日韩精品丝袜高跟鞋| 久久久国产一区二区三区四区小说| 国产精品国产精品国产专区不蜜| mm1313亚洲国产精品无码试看| 黄p免费网站| 国产日韩精品在线| 欧美一级片免费在线| 久久免费一级片| 呻吟揉丰满对白91乃国产区| 欧美日韩夫妻久久| 中文字幕日本精品| 日韩一级片网址| 亚洲娇小xxxx欧美娇小| 亚洲精品888| 亚洲成人a级片| 999精品网| 成人性色生活片免费看爆迷你毛片| 美女尤物在线视频| 成人亚洲精品久久久久软件| 蜜桃传媒在线观看免费进入| 午夜国产一区二区三区| 蜜桃极品自拍av| 北条麻妃99精品青青久久| 高清电影在线免费观看| 亚洲精品高清视频在线观看| 天堂av中文在线资源库| 日本久久亚洲电影| 丝袜美腿亚洲一区二区| 青草青草久热精品视频在线观看| 日韩精品xxxx| 国产精品久久97| 亚洲人成网站777色婷婷| 在线观看中文字幕亚洲| 欧美日一区二区三区| 久久精品国产大片免费观看| 免费av网站观看| 成熟丰满熟妇高潮xxxxx视频| 欧美日韩在线视频播放| 国产成人精品午夜| 欧美福利视频在线观看| 国产精品草草| 日韩av中文字幕一区| 国产精品日本一区二区| 国产麻豆精品久久| 91精品久久久久久| 久草精品视频在线观看| 日本不卡高字幕在线2019| 欧美成人乱码一区二区三区| 男生女生差差差的视频在线观看| 丰满少妇xoxoxo视频| 久久99国内| 欧美韩日一区二区| 91麻豆精品国产91久久久久久久久| 狠狠躁夜夜躁人人爽天天天天97| 1769免费视频在线观看| 天堂av在线一区| 国产综合精品在线| 日韩精品成人免费观看视频| 青青草精品在线视频| 91农村精品一区二区在线| 外国成人毛片| 国产精品99久久久久久人| 日本熟女毛茸茸| 黄色成人在线免费观看| c#hpsocket| 操碰免费视频| 欧美一区欧美二区| 国产精品精品一区二区三区午夜版| 成人国产一区二区三区精品| 欧美性久久久久| 欧美性猛交xxxx免费看蜜桃| 久久亚洲导航| 成人免费无码大片a毛片| 福利小视频网站| 男人皇宫亚洲男人2020| 日韩免费大片| 欧洲日本亚洲国产区| 精品视频在线观看一区二区| 中文字幕制服丝袜| 一二三四在线观看视频韩国| 青青视频免费在线观看| 国产白丝精品91爽爽久久| 亚洲免费视频二区| 少妇太紧太爽又黄又硬又爽| 亚洲无人区一区| 国产欧美日韩另类一区| 欧美成人在线直播| 26uuu亚洲伊人春色| 国产精品视频网站| 国产亚洲精品资源在线26u| 欧美国产一区二区| 色婷婷一区二区三区四区| 天堂网在线资源| 亚洲黄色片在线观看| 日韩电影在线观看一区二区| 精品国产一区二区三区四区精华| 欧美brazzers| 国产精品bbw一区二区三区| 亚洲欧美一区二区三区在线观看| 免费成人深夜夜行p站| 91蜜桃臀久久一区二区| 91精品国产乱码久久蜜臀| 一二三四社区欧美黄| 日韩情涩欧美日韩视频| 在线观看国产精品视频| 先锋影音一区二区三区| 亚洲精品国产美女| 国产美女精品视频国产| 国产+成+人+亚洲欧洲自线| 在线欧美三级| 国产欧美91| 免费一级在线观看| 国产福利热线视频| 无码人妻丰满熟妇区毛片18| 国产成人免费在线观看不卡| 国产精品蜜月aⅴ在线| 先锋影音久久久| 精品国产91乱码一区二区三区四区| 亚洲视频一区二区在线观看| 久久99这里只有精品| 51久久夜色精品国产麻豆| 国产精品日本一区二区| 一区二区三区在线观看国产| 在线观看国产免费视频| 国内自拍欧美| a毛片在线观看| 爽爽视频在线观看| 国产精品人成电影在线观看| 在线成人视屏| 美女胸又www又黄的网站| 日韩新的三级电影| 国产成人精品免费看| 美女国产一区二区| 婷婷另类小说| 老牛影视免费一区二区| 精品国产乱码久久久久久蜜臀网站| 成人午夜电影网站| 国产一级免费av| 日韩av福利在线观看| 欧美综合在线视频|