Postgresql 中文操作指南

34.10. Functions Associated with the COPY Command #

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 的当前实现中,只有二进制列出现在二进制副本中;因此,每列格式目前始终与整体格式匹配。)

34.10.1. Functions for Sending COPY Data #

这些函数用于在 COPY FROM STDIN 期间发送数据。当连接不在 COPY_IN 状态下调用时,它们会失败。

  • PQputCopyData #

    • COPY_IN 状态期间将数据发送到服务器。

int PQputCopyData(PGconn *conn,
                  const char *buffer,
                  int nbytes);
  • 将指定 buffernbytes 长度的 COPY 数据传输到服务器。如果数据已排队,则结果是 1,如果数据因缓冲区已满而未排队,则结果为零(这仅在非阻塞模式下才会发生),如果发生错误,结果为 -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 命令的最终结果状态。可以按照通常的方式等待此结果可用。然后返回到正常操作。

34.10.2. Functions for Receiving COPY Data #

这些函数用于在 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 有用。零的结果表示 COPY 仍在进行中,但尚未返回任何行(这仅在 async 为 true 时才可能)。-1 的结果表示 COPY 已完成。-2 的结果表示发生错误(请咨询 PQerrorMessage 以了解原因)。

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

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

34.10.3. Obsolete Functions for 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 的 null。)

    • PQputline #

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

int PQputline(PGconn *conn,
              const char *string);
  • 由一系列对 PQputline 的调用所发送的 COPY 数据流具有与 PQgetlineAsync 返回的数据流相同格式,但应用程序不必在每次 PQputline 调用中严格发送一行数据;在每次调用中发送部分行或多行都是可以的。

    • PQputnbytes #

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

int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);
  • 这与 PQputline 完全相同,不同之处在于发送的字节数是直接指定的,因此数据缓冲区不必以 null 结尾。在发送二进制数据时请使用此过程。

    • PQendcopy #

  • Synchronizes with the server.

int PQendcopy(PGconn *conn);
  • 此函数会一直等到服务器完成复制。应在使用 PQputline 向服务器发送最后一个字符串或使用 PQgetline 从服务器接收最后一个字符串时发出此函数。必须发出此函数,否则服务器将与客户端“不同步”。返回此函数后,服务器已准备好接收下一个 SQL 命令。如果成功完成,返回值为 0,否则为非零。(如果返回值为非零,请使用 PQerrorMessage 检索详细信息。)

  • 在使用 PQgetResult 时,应用程序应重复执行 PQgetline 以响应 PGRES_COPY_OUT 结果,然后在看到终止符行后执行 PQendcopy 。然后应返回到 PQgetResult 循环,直到 PQgetResult 返回 null 指针。同样, PGRES_COPY_IN 结果由一系列 PQputline 调用后跟 PQendcopy 处理,然后返回到 PQgetResult 循环。此安排将确保在一系列 SQL 命令中嵌入的 COPY 命令得到正确执行。

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

Note

在 PostgreSQL 协议 3.0 之前,应用程序必须明确以最后一行形式发送两个字符 \. 来向服务器指明它已完成发送 COPY 数据。虽然这仍有效,但它已弃用,并且可以在将来的版本中删除 \. 的特殊含义。在发送实际数据后调用 PQendcopy 已足够。