PostgreSQL COPY命令相關(guān)的函數(shù)

2021-09-02 11:42 更新
33.9.1. 用于發(fā)送COPY數(shù)據(jù)的函數(shù)
33.9.2. 用于接收COPY數(shù)據(jù)的函數(shù)
33.9.3. 用于COPY的廢棄函數(shù)

PostgreSQL中的COPY命令有用于libpq的對網(wǎng)絡(luò)連接讀出或者寫入的選項。這一節(jié)描述的函數(shù)允許應(yīng)用通過提供或者消耗已拷貝的數(shù)據(jù)來充分利用這個功能。

整個處理是應(yīng)用首先通過PQexec或者一個等效的函數(shù)發(fā)出 SQL COPY命令。 對這個命令的響應(yīng)(如果命令無誤)將是一個狀態(tài)代碼是PGRES_COPY_OUT或 者PGRES_COPY_IN(取決于指定的拷貝方向)的 PGresult對象。 應(yīng)用然后就應(yīng)該使用這一節(jié)的函數(shù)接收或者傳送數(shù)據(jù)行。在數(shù)據(jù)傳輸結(jié)束之后,另外一個PGresult對象會被返回以表明傳輸?shù)某晒蛘呤 ?它的狀態(tài)將是:PGRES_COMMAND_OK表示成功,PGRES_FATAL_ERROR表示發(fā)生了一些問題。 此時我們可以通過PQexec發(fā)出進一步的 SQL 命令(在COPY操作的處理過程中,不能用同一個連接執(zhí)行其它 SQL 命令)。

如果一個COPY命令是通過PQexec在一個可能包含額外命令的字符串中發(fā)出的,那么應(yīng)用在完成COPY序列之后必須繼續(xù)用 PQgetResult 取得結(jié)果。 只有在PQgetResult返回NULL時,我們才能確信PQexec的命令字符串已經(jīng)處理完畢, 并且可以安全地發(fā)出更多命令。

這一節(jié)的函數(shù)應(yīng)該只在從PQexecPQgetResult獲得了PGRES_COPY_OUTPGRES_COPY_IN結(jié)果狀態(tài)的后執(zhí)行。

一個承載了這些狀態(tài)值之一的PGresult對象攜帶了正在開始的COPY操作的一些額外數(shù)據(jù)。這些額外的數(shù)據(jù)可以用于那些與帶查詢結(jié)果的連接一起使用的函數(shù):

PQnfields

返回要拷貝的列(域)的個數(shù)。

PQbinaryTuples

0 表示整體拷貝格式都是文本(行用新行分隔,列用分隔字符分隔等等)。1 表示整體拷貝格式都是二進制。詳見COPY

PQfformat

返回與拷貝操的每列相關(guān)的格式代碼(0 是文本,1 是二進制)。當整體拷貝格式是文本時,那么每列的格式代碼將總是零,但是二進制格式可以同時支持文本和二進制列(不過,就目前的COPY實現(xiàn)而言,二進制拷貝中只會出現(xiàn)二進制列;所以目前每列的格式總是匹配總體格式)。

注意

這些額外的數(shù)據(jù)值只在使用協(xié)議 3.0 時可用。在使用協(xié)議 2.0 時,所有這些函數(shù)都返回 0。

33.9.1. 用于發(fā)送COPY數(shù)據(jù)的函數(shù)

這些函數(shù)用于在COPY FROM STDIN期間發(fā)送數(shù)據(jù)。如果在連接不是COPY_IN狀態(tài),調(diào)用它們會失敗。

PQputCopyData

COPY_IN狀態(tài)中向服務(wù)器發(fā)送數(shù)據(jù)。

int PQputCopyData(PGconn *conn,
                  const char *buffer,
                  int nbytes);

傳輸指定buffer中長度為nbytesCOPY數(shù)據(jù)到服務(wù)器。 如果數(shù)據(jù)被放在隊列中,結(jié)果是 1;如果因為緩沖區(qū)滿而無法被放在隊列中(只可能發(fā)生在連接是非阻塞模式時),那么結(jié)果是零;如果發(fā)生錯誤,結(jié)果為 -1(如果返回值為 -1,那么使用 PQerrorMessage 檢索細節(jié)。如果值是零,那么等待寫準備好然后重試)。

應(yīng)用可以把COPY數(shù)據(jù)流劃分成任意方便的大小放到緩沖區(qū)中。在發(fā)送時,緩沖區(qū)載荷的邊界沒有什么語意。數(shù)據(jù)流的內(nèi)容必須匹配COPY命令預(yù)期的數(shù)據(jù)格式;詳見COPY

PQputCopyEnd

COPY_IN狀態(tài)中向服務(wù)器發(fā)送數(shù)據(jù)結(jié)束的指示。

int PQputCopyEnd(PGconn *conn,
                 const char *errormsg);

如果errormsgNULL,則成功結(jié)束COPY_IN操作。如果errormsg不是NULLCOPY被強制失敗, errormsg 指向的字符串是錯誤消息(不過,我們不應(yīng)假定這個準確的錯誤信息將會從服務(wù)器傳回,因為服務(wù)器可能已經(jīng)因為其自身原因?qū)е?code class="command">COPY失敗。還要注意的是在使用 3.0 協(xié)議之前的連接時,強制失敗的選項是不能用的)。

如果終止消息被發(fā)送,則結(jié)果為 1;在非阻塞模式中,結(jié)果為 1 也可能只表示終止消息被成功地放在了發(fā)送隊列中 (在非阻塞模式中,要確認數(shù)據(jù)確實被發(fā)送出去,你應(yīng)該接著等待寫準備好并且調(diào)用PQflush,重復(fù)這些直到返回零)。 零表示該函數(shù)由于緩沖區(qū)滿而無法將該終止消息放在隊列中,這只會發(fā)生在非阻塞模式中(在這種情況下,等待寫準備好并且再次嘗試 PQputCopyEnd 調(diào)用)。 如果發(fā)生系統(tǒng)錯誤,則返回 -1,可以使用PQerrorMessage檢索詳情。

在成功調(diào)用PQputCopyEnd之后,調(diào)用PQgetResult獲取COPY命令的最終結(jié)果狀態(tài)。 我們可以用平常的方法來等待這個結(jié)果可用。然后返回到正常的操作。

33.9.2. 用于接收COPY數(shù)據(jù)的函數(shù)

這些函數(shù)用于在COPY TO STDOUT的過程中接收數(shù)據(jù)。如果連接不在COPY_OUT狀態(tài),那么調(diào)用它們將會失敗。

PQgetCopyData

COPY_OUT狀態(tài)下從服務(wù)器接收數(shù)據(jù)。

int PQgetCopyData(PGconn *conn,
                  char **buffer,
                  int async);

在一個COPY期間嘗試從服務(wù)器獲取另外一行數(shù)據(jù)。數(shù)據(jù)總是以每次一個數(shù)據(jù)行的方式被返回;如果只有一個部分行可用,那么它不會被返回。 成功返回一個數(shù)據(jù)行涉及到分配一塊內(nèi)存來保存該數(shù)據(jù)。buffer參數(shù)必須為非NULL*buffer被設(shè)置為指向分配到的內(nèi)存的指針,或者是在沒有返回緩沖區(qū)的情況下指向 NULL。 一個非NULL的結(jié)果緩沖區(qū)在不需要時必須用PQfreemem釋放。

在成功返回一行之后,返回的值就是該數(shù)據(jù)行里數(shù)據(jù)的字節(jié)數(shù)(將是大于零)。 被返回的字符串總是空終止的,雖然這可能只是對文本COPY有用。 一個零結(jié)果表示該COPY仍然在處理中,但是還沒有可用的行(只在async為真時才可能)。 一個 -1 結(jié)果表示COPY已經(jīng)完成。-2 結(jié)果表示發(fā)生了錯誤(參考PQerrorMessage獲取原因)。

async為真時(非零),PQgetCopyData將不會阻塞等待輸入; 如果COPY仍在處理過程中并且沒有可用的完整行,那么它將返回零 (在這種情況下等待讀準備好,然后在再次調(diào)用 PQgetCopyData 之前,調(diào)用PQconsumeInput )。 當async為假(零)時,PQgetCopyData將阻塞,直到數(shù)據(jù)可用或者操作完成。

PQgetCopyData返回 -1 之后,調(diào)用PQgetResult獲取COPY命令的最后結(jié)果狀態(tài)。 我們可以用平常的方法來等待這個結(jié)果可用。然后返回到正常的操作。

33.9.3. 用于COPY的廢棄函數(shù)

這些函數(shù)代表了以前的處理COPY的方法。盡管它們還能用,但是現(xiàn)在已經(jīng)被廢棄,因為它們的錯誤處理很糟糕、檢測結(jié)束數(shù)據(jù)的方法也不方便,并且缺少對二進制或非阻塞傳輸?shù)闹С帧?/p>

PQgetline

讀取一個以新行終止的字符行到(由服務(wù)器傳輸) 到一個長度為length的字符串緩沖區(qū)。

int PQgetline(PGconn *conn,
              char *buffer,
              int length);

這個函數(shù)拷貝最多length-1 個字符到該緩沖區(qū)中,并且把終止的新行轉(zhuǎn)換成一個零字節(jié)。 PQgetline在輸入結(jié)束時返回EOF,如果整行都被讀取則返回 0,如果緩沖區(qū)填滿了而還沒有遇到結(jié)束的新行則返回 1。

注意,應(yīng)用必須檢查是否一個新行包含兩個字符\.,這表明服務(wù)器 已經(jīng)完成了COPY命令的結(jié)果發(fā)送。如果應(yīng)用可能收到超過length-1 字符長的行, 我們就應(yīng)該確保正確識別\.行(例如,不要把一個長數(shù)據(jù)行的結(jié)束當作一個終止行)。

PQgetlineAsync

不阻塞地讀取一行COPY數(shù)據(jù)(由服務(wù)器傳輸)到一個緩沖區(qū)中。

int PQgetlineAsync(PGconn *conn,
                   char *buffer,
                   int bufsize);

這個函數(shù)類似于PQgetline,但是可以被用于那些必須異步讀取COPY數(shù)據(jù)的應(yīng)用, 也就是不阻塞的應(yīng)用。 在發(fā)出了COPY命令并得到了PGRES_COPY_OUT響應(yīng)之后, 應(yīng)用應(yīng)該調(diào)用PQconsumeInputPQgetlineAsync直到檢測到結(jié)束數(shù)據(jù)的信號。

不像PQgetline,這個函數(shù)負責檢測結(jié)束數(shù)據(jù)。

在每次調(diào)用時,如果libpq的輸入緩沖區(qū)中有一個完整的數(shù)據(jù)行可用,PQgetlineAsync都將返回數(shù)據(jù)。 否則,在剩余行到達之前不會返回數(shù)據(jù)。如果識別到拷貝數(shù)據(jù)結(jié)束的標志,此函數(shù)返回 -1;如果沒有可用數(shù)據(jù)則返回 0; 或者是給出一個正數(shù)給出被返回的字節(jié)數(shù)。如果返回 -1,調(diào)用者下一步必須調(diào)用PQendcopy,然后回到正常處理。

返回的數(shù)據(jù)將不超過一個數(shù)據(jù)行的范圍。如果可能,每次將返回一個完整行。但如果調(diào)用者提供的緩沖區(qū)太小不足以容下服務(wù)器發(fā)送的行,那么將返回部分行。對于文本數(shù)據(jù),這可以通過測試返回的最后一個字節(jié)是否\n來檢測(在二進制COPY中, 需要對COPY數(shù)據(jù)格式進行實際的分析,以便做相同的判斷)。被返回的字符串不是空結(jié)尾的(如果你想增加一個終止空,確保傳遞一個比實際可用空間少一字節(jié)的 bufsize )。

PQputline

向服務(wù)器發(fā)送一個空終止的字符串。如果 OK 則返回 0;如果不能發(fā)送字符串則返回EOF。

int PQputline(PGconn *conn,
              const char *string);

一系列PQputline調(diào)用發(fā)送的COPY數(shù)據(jù)流和PQgetlineAsync返回的數(shù)據(jù)具有相同的格式, 只是應(yīng)用不需要每次PQputline調(diào)用中發(fā)送剛好一個數(shù)據(jù)行;在每次調(diào)用中發(fā)送多行或者部分行都是可以的。

注意

PostgreSQL協(xié)議 3.0 之前,應(yīng)用必須顯式地發(fā)送兩個字符\.作為最后一行來指示服務(wù)器已經(jīng)完成發(fā)送COPY數(shù)據(jù)。 雖然這么做仍然有效,但是它已經(jīng)被廢棄并且\.的特殊含義可能在將來的版本中刪除。 在發(fā)送完實際數(shù)據(jù)之后, 調(diào)用PQendcopy就足夠了。

PQputnbytes

向服務(wù)器發(fā)送一個非空終止的字符串。如果 OK 則返回 0,如果不能發(fā)送字符串則返回EOF。

int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);

這個函數(shù)類似PQputline,除了數(shù)據(jù)緩沖區(qū)不需要是空終止,因為要發(fā)送的字節(jié)數(shù)是直接指定的。在發(fā)送二進制數(shù)據(jù)時使用這個過程。

PQendcopy

與服務(wù)器同步。

int PQendcopy(PGconn *conn);

這個函數(shù)等待服務(wù)器完成拷貝。當最后一個字符串已經(jīng)用PQputline發(fā)送給服務(wù)器時或者當最后一個字符串已經(jīng)用PQgetline從服務(wù)器接收到時,就會發(fā)出這個函數(shù)。 這個函數(shù)必須被發(fā)出,否則服務(wù)器將會和客戶端不同步。從這個函數(shù)返回后,服務(wù)器就已經(jīng)準備好接收下一個 SQL 命令了。函數(shù)成功完成時返回值為 0,否則返回非零值(如果返回值為非零值,用PQerrorMessage檢索詳情)。

在使用PQgetResult時,應(yīng)用應(yīng)該通過反復(fù)調(diào)用PQgetline并且在看到終止行后調(diào)用 PQendcopy 來響應(yīng)PGRES_COPY_OUT結(jié)果。 然后它應(yīng)該返回到PQgetResult循環(huán)直到PQgetResult返回一個空指針。 類似地,PGRES_COPY_IN結(jié)果會用一系列PQputline加上之后的PQendcopy來處理,然后返 回到PQgetResult循環(huán)。 這樣的安排將保證嵌入到一系列SQL命令中的COPY命令將被正確執(zhí)行。

舊的應(yīng)用很可能會通過PQexec提交一個COPY命令并且假定事務(wù)在PQendcopy之后完成。 只有在COPY是命令字符串中唯一的SQL命令時才能正確工作。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號