PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 31. libpq - C 库 | 下一页

# 31.9. 与`COPY`命令相关的函数

PostgreSQL里的`COPY`命令里有用于 libpq从网络连接读出或者写入的选项。 本节描述的函数允许应用通过提供或者消耗拷贝数据，充分利用这个功能。

整个过程是应用首先通过`PQexec`或者一个等效的函数发出 `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`实现， 在二进制拷贝里只出现二进制字段；所以目前每字段的格式总是匹配整体格式。）

> 注意: 这些额外的数据值只能在使用 3.0 版本的协议的时候获得。在使用 2.0 版本的协议时，所有这些函数都返回 0。

## 31.9.1. 用于发送`COPY`数据的函数

这些函数用于在`COPY FROM STDIN`过程中发送数据。 如果在连接不是处于`COPY_IN`状态下，它们会失败。

`PQputCopyData`

  在`COPY_IN`状态里向服务器发送数据。

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

  传输指定的`buffer`里的，长度为`nbytes`的`COPY` 数据到服务器。如果数据发送成功，结果是 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`失败。 还要注意的是在使用 3.0 版本之前的协议连接时，强制失败的选项是不能用的。）

  如果终止数据发送，则结果为 1，如果发送企图会阻塞（只有在连接是在非阻塞模式的情况下才可能出现这个情况）， 则为零，如果发生错误则返回 -1。（如果返回值是 -1，用`PQerrorMessage` 检索细节。如果值是零，那么等待写准备好然后重新尝试。）

  在成功调用`PQputCopyEnd`之后，调用`PQgetResult`获取`COPY` 命令的最终结果状态。我们可以用平常的方法来等待这个结果可用。然后返回到正常的操作。

## 31.9.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`命令的最后结果状态。我们可以用通常的方法等待这个结果可用。然后返回到正常操作。

## 31.9.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`调用中发送一个数据行；每次调用发送多行或者部分行都是可以的。

  > 注意: 在PostgreSQL协议3.0之前，应用必须明确的发送两个字符`\.` 作为行结束，向服务器表明它已经完成了发送`COPY`数据。虽然这个仍然工作， 但是已经废弃了，并且`\.`的特殊含义在将来的版本中有望删除。 在发送实际数据之后调用`PQendcopy`就足够了。

`PQputnbytes`

  向服务器发送一个非空结尾的字符串。成功时返回 0，如果不能发送字符串返回`EOF`。

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

  此函数类似`PQputline`，除了数据缓冲区不需要是空结尾的之外， 因为要发送的字节数是直接声明的。在发送二进制数据的时候使用这个过程。

`PQendcopy`

  与服务器同步。

      int PQendcopy(PGconn *conn);

  这个函数将等待直到服务器完成拷贝。你可以在用`PQputline` 向服务器发送完最后一个字符串后或者用`PGgetline` 从服务器获取最后一行字符串后调用它。我们必须调用这个函数，否则服务器可能会和前端 "不同步"。在这个函数返回后，服务器就已经准备好接收下一个 SQL 命令了。 成功时返回0，否则返回非零值。（如果返回值为非 0，用`PQerrorMessage`检索细节。）

  在使用`PQgetResult`时，应用应该对`PGRES_COPY_OUT` 的结果做出反应：重复调用`PQgetline`，并且在收到结束行时调用 `PQendcopy`。然后应该返回到`PQgetResult` 循环直到`PQgetResult`返回空指针。类似地，`PGRES_COPY_IN` 结果是用一系列`PQputline`调用最后跟着`PQendcopy`， 然后返回到`PQgetResult`循环。这样的排列将保证嵌入到一系列 SQL命令里的`COPY`命令将被正确执行。

  旧的应用大多通过`PQexec`提交一个`COPY` 命令并且假设在`PQendcopy`后事务完成。这样只有在`COPY` 是命令字串里的唯一的SQL命令时才能正确工作。

上一页 | 起始页 | 下一页
异步通知 | 上一级 | 控制函数