2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

32.10. 与 COPY 命令相关的函数 #

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

整个过程是,应用程序首先通过 PQexec 或其中一个等效函数发出 SQL COPY 命令。对此的响应(如果命令没有错误)将是一个 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 的实现中,二进制复制中只出现二进制列;因此,目前每列的格式始终与整体格式匹配。)

32.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 检索详细信息。如果值为零,则等待数据可写并重试。)

应用程序可以将 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 命令的最终结果状态。可以按常规方式等待此结果可用。然后返回到正常操作。

32.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 的结果缓冲区。

当成功返回一行时,返回值是该行中数据的字节数(始终大于零)。返回的字符串始终以 null 结尾,尽管这可能仅对文本 COPY 有用。返回 0 表示 COPY 仍在进行中,但尚未准备好行(仅当 async 为 true 时才可能)。返回 -1 表示 COPY 已完成。返回 -2 表示发生错误(请参阅 PQerrorMessage 获取原因)。

async 为 true(非零)时,PQgetCopyData 不会阻塞等待输入;如果 COPY 仍在进行中但尚未准备好完整行,它将返回零。(在这种情况下,等待数据可读,然后调用 PQconsumeInput,然后再调用 PQgetCopyData。)当 async 为 false(零)时,PQgetCopyData 将阻塞直到有数据可用或操作完成。

PQgetCopyData 返回 -1 后,调用 PQgetResult 以获取 COPY 命令的最终结果状态。可以按常规方式等待此结果可用。然后返回到正常操作。

32.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 不同,此函数负责检测数据结束。

每次调用时,如果 libpq 的输入缓冲区中有完整的数据行可用,PQgetlineAsync 就会返回数据。否则,直到行的其余部分到达才会返回数据。如果识别到复制数据结束标记,则函数返回 -1;如果没有数据可用,则返回 0;否则返回一个正数,表示返回的数据字节数。如果返回 -1,调用者随后必须调用 PQendcopy,然后返回到正常处理。

返回的数据不会超出数据行的边界。如果可能,将一次返回一整行。但是,如果调用者提供的缓冲区太小而无法容纳服务器发送的一行,则将返回部分数据行。对于文本数据,可以通过测试最后一个返回的字节是否为 \n 来检测。(在二进制 COPY 中,需要实际解析 COPY 数据格式才能做出等效的判断。)返回的字符串不是 null 结尾的。(如果要添加终止 null,请确保将 bufsize 设置为比实际可用空间小一。)

PQputline #

将一个 null 结尾的字符串发送到服务器。如果成功则返回 0,如果无法发送字符串则返回 EOF

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

通过一系列 PQputline 调用发送的 COPY 数据流的格式与 PQgetlineAsync 返回的格式相同,不同之处在于应用程序不必每调用一次 PQputline 就发送正好一个数据行;每次调用发送部分行或多行是允许的。

注意

PostgreSQL 协议 3.0 之前,应用程序必须显式发送两个字符 \. 作为最后一行,以向服务器指示其已完成发送 COPY 数据。虽然这仍然有效,但已弃用,并且 \. 的特殊含义预计将在未来版本中被移除。(它在 CSV 模式下可能已出现问题。)在发送实际数据后调用 PQendcopy 就足够了。

PQputnbytes #

将一个非 null 结尾的字符串发送到服务器。如果成功则返回 0,如果无法发送字符串则返回 EOF

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

这与 PQputline 完全相同,不同之处在于数据缓冲区不必是 null 结尾的,因为要发送的字节数是直接指定的。发送二进制数据时使用此过程。

PQendcopy #

与服务器同步。

int PQendcopy(PGconn *conn);

此函数等待直到服务器完成复制。当使用 PQputline 向服务器发送最后一条字符串,或者使用 PQgetline 从服务器接收最后一条字符串时,都应发出此函数。必须发出此函数,否则服务器将与客户端 失去同步。此函数返回后,服务器就可以接收下一个 SQL 命令了。返回值是 0 表示成功完成,非零表示失败。(如果返回值非零,请使用 PQerrorMessage 检索详细信息。)

当使用 PQgetResult 时,应用程序应响应 PGRES_COPY_OUT 结果,通过反复执行 PQgetline,然后在看到终止符行后执行 PQendcopy。然后应返回到 PQgetResult 循环,直到 PQgetResult 返回 null 指针。类似地,PGRES_COPY_IN 结果通过一系列 PQputline 调用,然后执行 PQendcopy 来处理,然后返回到 PQgetResult 循环。这种安排将确保一系列命令中嵌入的 COPY 命令SQL能够正确执行。

旧的应用程序很可能会通过 PQexec 提交 COPY,并在 PQendcopy 之后假设事务已完成。只有当 COPY 是命令字符串中的唯一SQL命令时,这才会正常工作。

提交更正

如果您在文档中发现任何不正确、与您对特定功能的使用经验不符或需要进一步说明的内容,请使用 此表格 报告文档问题。