PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

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

32.10.1. 发送 COPY 数据的函数
32.10.2. 接收 COPY 数据的函数
32.10.3. 已过时的 COPY 相关函数

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 操作的一些额外数据。 这些额外数据可以用与查询结果一起使用的那些函数获得:

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);

把 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 命令的最终结果状态。 可以按照通常的方式等待这个结果可用。 然后返回正常操作。

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。 一个非 NULL 的结果缓冲区在不再需要时应当用 PQfreemem 释放。

当一行被成功返回时, 返回值是该行中数据字节的数目 (它总是大于零)。 返回的字符串总是以空字符结尾的, 虽然这可能只对文本 COPY 有用。 结果为零表示 COPY 仍在进行中, 但尚无行可用 (这只在 async 为真时才可能)。 结果为 -1 表示 COPY 已完成。 结果为 -2 表示发生了错误 (咨询 PQerrorMessage 获取原因)。

当 async 为真(非零)时, PQgetCopyData 不会阻塞着等待输入; 如果 COPY 仍在进行中但没有完整的行可用, 它会返回零。 (在这种情况下, 等待读就绪, 然后在再次调用 PQgetCopyData 之前调用 PQconsumeInput 。) 当 async 为假(零)时, 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 响应之后, 应用程序应当调用 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 调用时恰好发送一条数据行; 每次调用发送一个部分行或多条行都是可以的。

Note

在 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 命令时, 这才能正确工作。