Skip to content

34.10. 与COPY命令关联的函数#

34.10.1. 发送 COPY 数据的函数
34.10.2. 接收 COPY 数据的函数
34.10.3. COPY 的废弃函数

在PostgreSQL中,COPY命令具有从libpq所使用的网络连接读取或写入该连接的选项。本部分中介绍的函数允许应用程序通过提供或使用已复制的数据来利用此功能。

整个过程是应用程序首先通过PQexec或其中一个等效函数发出 SQLCOPY命令。对此的响应(如果命令中没有错误)将是PGresult对象,其状态代码为PGRES_COPY_OUTPGRES_COPY_IN(具体取决于指定的复制方向)。然后,应用程序应使用本部分的函数来接收或传输数据行。当数据传输完成时,将返回另一个PGresult对象以指示传输成功或失败。如果成功,其状态将为PGRES_COMMAND_OK;如果遇到某些问题,其状态将为PGRES_FATAL_ERROR。此时,可以通过PQexec发出其他 SQL 命令。(在COPY操作进行期间,无法使用同一连接执行其他 SQL 命令。)

如果通过PQexec在可能包含其他命令的字符串中发出了COPY命令,应用程序必须在完成COPY序列后通过PQgetResult继续获取结果。只有当PQgetResult返回NULL时,才能确定PQexec命令字符串已完成,并且可以安全地发出更多命令。

本节中的函数应仅在从PQexecPQgetResult获得PGRES_COPY_OUTPGRES_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);

如果errormsgNULL,则成功结束 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 响应后,应用程序应调用 PQconsumeInputPQgetlineAsync,直到检测到数据结束信号。

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 是命令字符串中唯一的 命令时,此操作才会正常工作。