27.7. 與COPY命令相關的函數

PostgreSQL 裡的 COPY 命令裡有用于 libpq 裡從網絡連接讀出或者寫入的選項。 本節描述的函數允許應用通過提供或者消耗拷貝數據,充分利用這個功能。

整個過程是應用首先通過 PQexec 或者一個等效的函數發出 SQL COPY 命令。 對這個命令的相應(如果命令無誤)將是一個帶著狀態碼 PGRES_COPY_OUT 或者 PGRES_COPY_INPGresult(具體根據聲明的拷貝方向)。 應用然後就應該使用本節的函數接受或者發送數據行。在數據傳輸結束之後,返回另外一個 PGresult 對象以表明傳輸的成功或者失敗。它的狀態將是 PGRES_COMMAND_OK 表示成功或者 如果發生了一些問題,是 PGRES_FATAL_ERROR。 這個時候開始我們可以通過 PQexec 發出更多 SQL 命令。 (在 COPY 操作在處理的過程中,我們不可能用同一個連接執行其它 SQL 命令。

如果一個 COPY 是通過 PQexec 在一個可以包含額外命令的字串裡發出的, 那麼應用在完成 COPY 序列之後必須繼續用 PQgetResult 抓取結果。 只有在 PQgetResult 返回 NULL 的時候,我們才能確信 PQexec 的命令字串已經處理完畢,並且已經可以安全地發出更多命令。

本節地這些函數應該只在從 PQexecPQgetResult 獲得了 PGRES_COPY_OUTPGRES_COPY_IN 結果狀態的情況下執行。

一個承載了這些狀態值之一地 PGresult 對象運載了某些有關正在開始地 COPY 操作的額外信息。這些額外的數據可以用那些同時也處理查詢 結果的函數獲取。

PQnfields

返回要拷貝的字段(數據域)個數。

PQbinaryTuples

0 表示全部拷貝格式都是文本的(行之間用換行分隔,字段用分隔符分隔,等等)。 1 表示全部拷貝格式都是二進制。參閱 COPY 獲取更多信息。

PQfformat

返回和拷貝操作的每個字段相關的格式代碼(0 是文本,1 是二進制)。 如果全部拷貝格式是文本,那麼每字段的格式碼將總是零,但是(總體)二進制格式可以支持文本和二進制字段並存。 (不過,就目前的 COPY 實現,在二進制拷貝裡只出現二進制字段; 所以目前每字段的格式總是匹配縱起格式。)

注意: 這些額外的數據值只能在使用 3.0 版本的協議的時候獲得。在使用 2.0 版本的協議時,所有這些函數都返回 0。

27.7.1. 用于發送 COPY 數據的函數

這些函數用于在 COPY FROM STDIN 過程中發送數據。 如果在連接不是處于 COPY_IN 狀態下,它們會失敗。

PQputCopyData

COPY_IN 狀態期間向服務器發送數據。

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

傳輸指定的 buffer 裡的,長度為 nbytesCOPY 數據到服務器。 如果數據發送成功,結果是 1,如果因為發送企圖會阻塞(這種情況只有在連接是非阻塞模式時才有可能)而沒有成功, 那麼是零,或者是在發生錯誤的時候是 -1。(如果返回 -1,那麼使用 PQerrorMessage 檢索細節。 如果值是零,那麼等待寫準備好然後重試。)

應用可以把 COPY 數據流分隔成任意合適的大小放到緩衝區裡。 在發送的時候,緩衝區的邊界沒有什麼特殊的語意。數據流的內容必須匹配 COPY 命令預期的數據格式; 參閱 COPY 獲取細節。

PQputCopyEnd

COPY_IN 狀態裡向服務器發送數據完畢的指示。

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

如果 errormsgNULL,則成功結束 COPY_IN 操作。 如果 errormsg 不是 NULLCOPY 操作被強制失敗, errormsg 指向的字串是錯誤信息。(我們不能認為同樣的信息可能會從服務器傳回, 因為服務器可能已經因為自己的原因讓 COPY 失敗。還要注意的是在使用 3.0 版本之前的協議連接時, 強制失敗的選項是不能用的。)

如果終止數據發送,則結果為 1,如果發送企圖會阻塞(只有在連接是在非阻塞模式的情況下才可能出現這個情況), 則為零,如果發生錯誤則返回 -1。(如果返回值是 -1,用 PQerrorMessage 檢索細節。 如果值是零,那麼等待寫準備好然後重新嘗試。)

在成功調用 PQputCopyEnd 之後,調用 PQgetResult 獲取 COPY 命令的最終結果狀態。 我們可以用平常的方法來等待這個結果可用。然後返回到正常的操作。

27.7.2. 用于接收 COPY 數據的函數

這些函數用于在 COPY TO STDOUT 的過程中檢索數據。 如果連接不在 COPY_OUT 狀態,那麼他們將會失敗。

PQgetCopyData

COPY_OUT 狀態下從服務器接收數據。

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

在一個 COPY 的過程中試圖獲取另外一行數據。 數據總是每次返回一個數據行;如果只有一行的部分可用,那麼它不會被返回。 成功返回一個數據行包括分配一個內存塊來保存這些數據。 buffer 參數必須是非 NULL*buffer 設置為指向分配出來的內存的指針,或者是如果沒有返回緩衝區,那麼為 NULL。 一個非NULL的結果緩衝區在不再需要的時候必須用 PQfreemem 釋放。

在成功返回一行之後,那麼返回的值就是該數據行裡數據的字節數(這個將總是大于零)。 返回的字串總是空結尾的,雖然可能只是對文本的 COPY 有用。 一個零的結果表示該 COPY 仍然在處理中,但是還沒有可以用的行(這個只有在 async 為真的時候才可能)。 一個結果為 -1 的值表示 COPY 已經結束。結果為 -2 表示發生了錯誤(參考 PQerrorMessage 獲取原因)。

async 為真的時候(非零),PQgetCopyData 將不會阻塞住等待輸入; 如果該 COPY 仍在處理過程中並且沒有可用的完整行,那麼它將返回零。 (在這種情況下它在重新嘗試之前等待讀準備好;它並不關心你是否調用了 PQconsumeInput。) 在 async 是假(零)的時候,PQgetCopyData 將阻塞住,知道有可用的數據或者操作完成。

PQgetCopyData 返回 -1 之後,調用 PQgetResult 獲取 COPY 命令的最後結果狀態。 我們可以用通常的方法等待這個結果可用。然後返回到正常操作。

27.7.3. 用于 COPY 的廢棄的函數

下面的這些函數代表了以前的處理 COPY 的方法。 盡管他們還能用,但是現在已經廢棄了,因為他們的錯誤處理實在是太糟糕了, 並且檢測數據結束的方法也很不方便,並且缺少對二進制何非阻塞傳輸的支持。

PQgetline

讀取一個以新行符結尾的字符行中指定字節數的字符(由服務器服務器傳輸)到一個長度為 length 的字符串緩衝區。

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

這個函數拷貝最多length-1 個字符到緩衝區裡,然後把終止的新行符轉換成一個字節零。 PQgetline 在輸入結束時返回 EOF, 如果整行都被讀取了返回 0,如果緩衝區填滿了而還沒有遇到結束的新行符則返回 1。

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

PQgetlineAsync

不做阻塞地讀取一行 COPY 數據(由服務器服務器傳輸)到一個緩衝區中。

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

這個函數類似于 PQgetline,但是可以用于那些必須異步地讀取 COPY數據的應用,也就是不阻塞的應用。在使用了COPY命令和獲取了 PGRES_COPY_OUT響應之後,應用應該調用 PQconsumeInputPQgetlineAsync 直到收到數據結束的信號。

不象PQgetline,這個函數負責檢測數據結束。

在每次調用時,如果libpq 的輸入緩衝區內有可用的一個完整的換行符結尾的數據行, PQgetlineAsync 都將返回數據。 否則,在其他數據到達之前不會返回數據。 如果見到了拷貝數據結束的標志,此函數返回 -1,如果沒有可用數據返回 0, 或者是給出一個正數表明返回的數據的字節數。如果返回 -1,調用者下一步必須調用 PQendcopy,然後回到正常處理。

返回的數據將不超過一行的範圍。 如果可能,每次將返回一個完整行。但如果調用者提供的緩衝區太小, 無法容下服務器發出的整行,那麼將返回部分行。這個可以通過測試返回的最後一個字節是否 \n 來確認。 在二進制 COPY 中,我們需要對 COPY 數據格式進行實際的分析,以便做相同的判斷。) 返回的字符串不是空結尾的。 (如果你想得到一個空結尾的字串,確保你傳遞了一個 比實際可用空間少一字節的bufsize。)

PQputline

向服務器服務器發送一個空結尾的字符串。成功時返回 0,如果不能發送字符串返回 EOF

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

一系列 PQputline 調用發送的 COPY 數據流和 PQgetlineAsync 返回的數據有著一樣的格式, 只是應用不需要明確地在每次 PQputline 調用中發送一個數據行;我們每次調用裡發送多行或者部分行都是可以的。

注意: PostgreSQL 3.0 版本的協議之前,應用必須明確地發送兩個字符 \. 給服務器, 告訴服務器它已經完成 COPY 數據的發送。雖然這麼做仍然有效,但是已經廢棄了, \. 的特殊含義可能在將來的版本中刪除。在發送完實際數據之後,調用 PQendcopy 就足夠了。

PQputnbytes

向服務器服務器發送一個非空結尾的字符串。成功時返回 0,如果不能發送字符串返回 EOF

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

此函數類似 PQputline,除了數據緩衝區不需要是空結尾的之外,因為要發送的字節數是直接聲明的。 在發送二進制數據的時候使用這個過程。

PQendcopy

與服務器同步。

int PQendcopy(PGconn *conn);

這個函數等到服務器完成拷貝(才返回?)。 你可以在用 PQputline 向服務器發送完最後一個字符串後或者用 PGgetline從服務器獲取最後一行字符串後調用它。 我們必須調用這個函數,否則服務器可能會和前端"同步丟失"。在這個函數返回後, 服務器就已經準備好接收下一個 SQL 命令了。成功時返回0,否則返回非零值。 (如果返回值為非 0,用 PQerrorMessage 檢索細節。)

在使用 PQgetResult時,應用應該對 PGRES_COPY_OUT 的結果做出反應:重復調用 PQgetline,並且在收到結束行時調用 PQendcopy。 然後應該返回到 PQgetResult 循環直到 PQgetResult 返回 NULL。 類似地, PGRES_COPY_IN 結果是用一系列 PQputline 調用最後跟著 PQendcopy,然後返回到 PQgetResult 循環。 這樣的排列將保證嵌入到一系列 SQL 命令裡的 COPY 命令將被正確執行。

舊的應用大多通過 PQexec 提交一個 COPY 命令並且假設在 PQendcopy 後事務完成。 這樣只有在 COPY 是命令字串裡的唯一的 SQL 命令時才能正確工作。