34.10. 与COPY
命令关联的函数#
在PostgreSQL中,COPY
命令具有从libpq所使用的网络连接读取或写入该连接的选项。本部分中介绍的函数允许应用程序通过提供或使用已复制的数据来利用此功能。
整个过程是应用程序首先通过PQexec
或其中一个等效函数发出 SQLCOPY
命令。对此的响应(如果命令中没有错误)将是PGresult
对象,其状态代码为PGRES_COPY_OUT
或PGRES_COPY_IN
(具体取决于指定的复制方向)。然后,应用程序应使用本部分的函数来接收或传输数据行。当数据传输完成时,将返回另一个PGresult
对象以指示传输成功或失败。如果成功,其状态将为PGRES_COMMAND_OK
;如果遇到某些问题,其状态将为PGRES_FATAL_ERROR
。此时,可以通过PQexec
发出其他 SQL 命令。(在COPY
操作进行期间,无法使用同一连接执行其他 SQL 命令。)
如果通过PQexec
在可能包含其他命令的字符串中发出了COPY
命令,应用程序必须在完成COPY
序列后通过PQgetResult
继续获取结果。只有当PQgetResult
返回NULL
时,才能确定PQexec
命令字符串已完成,并且可以安全地发出更多命令。
本节中的函数应仅在从PQexec
或PQgetResult
获得PGRES_COPY_OUT
或PGRES_COPY_IN
的结果状态后执行。
带有这些状态值之一的PGresult
对象会携带有关正在启动的COPY
操作的一些附加数据。可以使用与查询结果一起使用的函数获取此附加数据
PQnfields
#返回要复制的列(字段)数。
PQbinaryTuples
#0 表示整体复制格式为文本(行由换行符分隔,列由分隔符分隔,等等)。1 表示整体复制格式为二进制。有关更多信息,请参见 COPY。
PQfformat
#返回与复制操作的每列关联的格式代码(文本为 0,二进制为 1)。当整体复制格式为文本时,每列的格式代码始终为零,但二进制格式可以同时支持文本和二进制列。(但是,在
COPY
的当前实现中,只有二进制列出现在二进制副本中;因此,每列的格式目前始终与整体格式匹配。)
34.10.1. 用于发送COPY
数据的函数#
这些函数用于在COPY FROM STDIN
期间发送数据。如果在连接不在COPY_IN
状态时调用它们,它们将失败。
PQputCopyData
#在
COPY_IN
状态期间向服务器发送数据。int PQputCopyData(PGconn *conn, const char *buffer, int nbytes);
将指定
buffer
中的COPY
数据(长度为nbytes
)传输到服务器。如果数据已排队,则结果为 1;如果由于缓冲区已满而未排队(这仅在非阻塞模式下发生),则结果为 0;如果发生错误,则结果为 -1。(如果返回值为 -1,请使用PQerrorMessage
检索详细信息。如果值为 0,请等待写入就绪并重试。)应用程序可以将
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
命令的最终结果状态。可以按照通常的方式等待此结果可用。然后返回正常操作。
34.10.2. 用于接收COPY
数据的函数#
这些函数用于在COPY TO STDOUT
期间接收数据。如果在连接不在COPY_OUT
状态时调用它们,它们将失败。
PQgetCopyData
#在
COPY_OUT
状态期间从服务器接收数据。int PQgetCopyData(PGconn *conn, char **buffer, int async);
尝试在
COPY
期间从服务器获取另一行数据。一次始终返回一行数据;如果仅有一部分行可用,则不返回该行。成功返回数据行涉及分配一块内存来保存数据。buffer
参数必须为非NULL
。*buffer
设置为指向已分配的内存,或在不返回缓冲区的情况下设置为NULL
。不再需要时,应使用PQfreemem
释放非NULL
结果缓冲区。成功返回一行时,返回值是行中的数据字节数(这始终大于零)。返回的字符串始终以空结尾,尽管这可能仅对文本
COPY
有用。结果为零表示COPY
仍在进行中,但尚未提供任何行(仅当async
为 true 时才可能出现这种情况)。结果为 -1 表示COPY
已完成。结果为 -2 表示发生错误(请查阅PQerrorMessage
以了解原因)。当
async
为 true(非零)时,PQgetCopyData
不会阻塞等待输入;如果COPY
仍在进行中,但没有完整的行可用,它将返回零。(在这种情况下,等待读取就绪,然后调用PQconsumeInput
,然后再调用PQgetCopyData
。)当async
为 false(零)时,PQgetCopyData
将阻塞,直到数据可用或操作完成。在
PQgetCopyData
返回 -1 后,调用PQgetResult
以获取COPY
命令的最终结果状态。可以按通常的方式等待此结果可用。然后返回正常操作。
34.10.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
响应后,应用程序应调用PQconsumeInput
和PQgetlineAsync
,直到检测到数据结束信号。与
PQgetline
不同,此函数负责检测数据结束。每次调用时,
PQgetlineAsync
将在 libpq 的输入缓冲区中提供完整数据行时返回数据。否则,在行的其余部分到达之前,不会返回任何数据。如果已识别出复制数据结束标记,则该函数返回 -1,如果没有可用数据,则返回 0,或返回一个正数,给出返回的数据字节数。如果返回 -1,则调用方必须接下来调用PQendcopy
,然后返回到正常处理。返回的数据不会超出数据行边界。如果可能,一次将返回一整行。但是,如果调用方提供的缓冲区太小,无法容纳服务器发送的行,则将返回部分数据行。对于文本数据,可以通过测试最后一个返回的字节是否是
\n
来检测这种情况。(在二进制COPY
中,需要实际解析COPY
数据格式才能做出等效的判断。)返回的字符串不是以 null 结尾的。(如果你想添加一个终止 null,请务必传递一个比实际可用空间小一个的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
向服务器发送最后一个字符串或使用PQgetline
从服务器接收最后一个字符串时,应发出此函数。必须发出此函数,否则服务器将与客户端“不同步”。此函数返回后,服务器将准备好接收下一个 SQL 命令。如果成功完成,则返回值为 0,否则为非零。(如果返回值为非零,请使用PQerrorMessage
检索详细信息。)使用
PQgetResult
时,应用程序应通过重复执行PQgetline
响应PGRES_COPY_OUT
结果,然后在看到终止符行后执行PQendcopy
。然后,它应返回到PQgetResult
循环,直到PQgetResult
返回一个空指针。类似地,PGRES_COPY_IN
结果通过一系列PQputline
调用处理,然后是PQendcopy
,然后返回到PQgetResult
循环。此安排将确保正确执行嵌入在一系列 命令中的COPY
命令。较旧的应用程序可能会通过
PQexec
提交COPY
,并假设在PQendcopy
之后事务完成。仅当COPY
是命令字符串中唯一的 命令时,此操作才会正常工作。