PostgreSQL
中的 COPY
命令带有从
libpq
使用的网络连接读取或向其写入的选项。
本节描述的函数让应用程序能够通过提供或消费被拷贝的数据来利用
这项能力。
总体过程是:
应用程序首先通过
PQexec
或某个等效函数发出 SQL
COPY 命令。
对它的响应
(如果命令中没有错误)
将是一个带有状态码
PGRES_COPY_OUT 或
PGRES_COPY_IN
(取决于指定的拷贝方向)
的 PGresult
对象。
然后应用程序应当使用本节的函数来接收或传输数据行。
当数据传输完成时,
会返回另一个
PGresult
对象来指示传输的成功或失败。
成功时它的状态将是
PGRES_COMMAND_OK,
遇到某些问题时为
PGRES_FATAL_ERROR。
此时可以通过
PQexec
发出更多 SQL 命令。
(在
COPY
操作进行期间,
不可能用同一条连接执行其他 SQL
命令。)
如果一条
COPY
命令在一个可能包含额外命令的字符串中通过
PQexec
被发出,
应用程序必须在完成
COPY
序列之后继续通过
PQgetResult
获取结果。
只有当
PQgetResult
返回
NULL 时,
才能确定
PQexec
命令字符串已经完成,
并且发出更多命令才是安全的。
本节的函数应当只在从
PQexec 或
PQgetResult
获得了
PGRES_COPY_OUT 或
PGRES_COPY_IN
的结果状态之后执行。
带有这些状态值之一的
PGresult
对象携带着关于正在开始的
COPY
操作的一些额外数据。
这些额外数据可以用与查询结果一起使用的那些函数获得:
这些函数用于在
COPY FROM
STDIN
期间发送数据。
如果在连接不处于
COPY_IN
状态时调用它们,
它们将失败。
PQputCopyData #
在 COPY_IN
状态期间向服务器发送数据。
int PQputCopyData(PGconn *conn,
const char *buffer,
int nbytes);
把 COPY
数据(位于长度为
nbytes
的指定
buffer 中)
传输给服务器。
如果数据被排队,
结果为 1;
如果因为缓冲区满而没有被排队则为零
(这只会在非阻塞模式下发生);
如果发生了错误则为 -1。
(如果返回值是 -1,
使用
PQerrorMessage
检索细节。
如果值是零,
等待写就绪并重试。)
应用程序可以把
COPY
数据流划分成任意方便大小的缓冲区载荷。
在发送时,
缓冲区载荷的边界没有语义上的意义。
数据流的内容必须与
COPY
命令期望的数据格式相匹配;
细节请参见 COPY。
PQputCopyEnd #
在 COPY_IN
状态期间向服务器发送数据结束指示。
int PQputCopyEnd(PGconn *conn,
const char *errormsg);
如果 errormsg
为 NULL,
则成功结束
COPY_IN
操作。
如果 errormsg
不为 NULL,
那么 COPY
会被强制失败,
以 errormsg
指向的字符串作为错误消息。
(不过,
不应当假定这个确切的错误消息会从服务器返回,
因为服务器可能已经出于它自己的原因让
COPY
失败了。)
如果终止消息被发送,
结果为 1;
或者在非阻塞模式下,
这可能只表示终止消息被成功排队。
(在非阻塞模式下,
要确定数据已被发送,
你接下来应当等待写就绪并调用
PQflush,
重复直到它返回零。)
零表示由于缓冲区满该函数无法把终止消息排队;
这只会在非阻塞模式下发生。
(在这种情况下,
等待写就绪并再次尝试
PQputCopyEnd
调用。)
如果发生硬错误,
则返回 -1;
你可以使用
PQerrorMessage
检索细节。
在成功调用
PQputCopyEnd
之后,
调用
PQgetResult
来获得
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
仍在进行中但没有完整的行可用,
它会返回零。
(在这种情况下,
等待读就绪,
然后在再次调用
PQgetCopyData
之前调用
PQconsumeInput
。)
当 async
为假(零)时,
PQgetCopyData
将阻塞到数据可用或操作完成为止。
在 PQgetCopyData
返回 -1 之后,
调用
PQgetResult
来获得
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
响应之后,
应用程序应当调用
PQconsumeInput
和
PQgetlineAsync
直到检测到数据结束信号。
与 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
数据的发送。
虽然这样做仍然可行,
但它已被弃用,
并且
\.
的特殊含义可以预期会在未来的发行版中被移除。
(在
CSV
模式下它已经会行为不当。)
在发送完实际数据之后调用
PQendcopy
就足够了。
PQputnbytes #
向服务器发送一个不以空字符结尾的字符串。
成功返回 0,
如果无法发送该字符串则返回
EOF。
int PQputnbytes(PGconn *conn,
const char *buffer,
int nbytes);
这与 PQputline
完全一样,
只是数据缓冲区不需要以空字符结尾,
因为要发送的字节数是直接指定的。
在发送二进制数据时使用这个过程。
PQendcopy #与服务器同步。
int PQendcopy(PGconn *conn);
这个函数会等待服务器完成拷贝。
它应当在用
PQputline
把最后一个字符串发送给服务器之后,
或者在用
PQgetline
从服务器收到最后一个字符串之后被发出。
它必须被发出,
否则服务器将与客户端
“失去同步”。
从这个函数返回之后,
服务器就准备好接收下一条 SQL
命令了。
成功完成时返回值为 0,
否则为非零。
(如果返回值非零,
使用
PQerrorMessage
检索细节。)
在使用
PQgetResult
时,
应用程序应当通过反复执行
PQgetline、
然后在看到终止行之后执行
PQendcopy
来响应一个
PGRES_COPY_OUT
结果。
然后它应当返回
PQgetResult
循环,
直到
PQgetResult
返回一个空指针。
类似地,
一个
PGRES_COPY_IN
结果通过一系列
PQputline
调用加上
PQendcopy
来处理,
然后返回
PQgetResult
循环。
这种安排将确保嵌入在一系列
SQL
命令中的一条
COPY
命令会被正确执行。
较老的应用程序很可能通过
PQexec
提交一条
COPY,
并假定在
PQendcopy
之后事务就完成了。
只有当
COPY
是命令字符串中唯一的
SQL
命令时,
这才能正确工作。