34.9. 与 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序列后 continue 通过PQgetResult提取结果。仅当PQgetResult返回NULL时,才能确定PQexec命令字符串已完成,并且可以发出更多命令。

只有从PQexecPQgetResult获得结果状态为PGRES_COPY_OUTPGRES_COPY_IN后,才应执行本节的功能。

带有这些状态值之一的PGresult对象携带一些有关正在启动的COPY操作的附加数据。这些附加数据可通过与查询结果结合使用的功能获得:

  • PQnfields

    • 返回要复制的列(字段)数。
  • PQbinaryTuples

    • 0 表示整体复制格式为文本格式(行由换行符分隔,列由分隔符分隔,等等)。 1 表示整体复制格式为二进制。有关更多信息,请参见COPY
  • PQfformat

    • 返回与复制操作的每一列关联的格式代码(0 表示文本,1 表示二进制)。当整体复制格式为文本时,每列格式代码将始终为零,但二进制格式可以同时支持文本列和二进制列。 (但是,从当前的COPY实现开始,二进制副本中仅显示二进制列;因此,当前每列格式始终与总体格式匹配.)

Note

这些附加数据值仅在使用协议 3.0 时可用。使用协议 2.0 时,所有这些函数将返回 0.

34 .9.1. 发送 COPY 数据的功能

这些功能用于在COPY FROM STDIN期间发送数据。如果在连接未处于COPY_IN状态时调用它们,它们将失败。

  • PQputCopyData
    • COPY_IN状态下将数据发送到服务器。
int PQputCopyData(PGconn *conn,
                  const char *buffer,
                  int nbytes);

将指定的* buffer *中长度为nbytes *的COPY数据传输到服务器。如果数据已排队,则结果为 1;如果由于缓冲区已满而未排队,则结果为 0(这只会在非阻塞模式下发生);如果发生错误,则结果为-1. (如果返回值为-1,请使用PQerrorMessage来检索详细信息.如果该值为零,请 await 写就绪,然后重试.)

应用程序可以将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;否则,结果为 0.或在非阻止模式下,这可能仅表示终止消息已成功排队。 (在非阻塞模式下,为确定已发送数据,您应接着 await 写就绪并调用PQflush,直到返回零为止.)零表示由于缓冲区已满,该函数无法将终止消息排队;否则,返回 0.这只会在非阻止模式下发生。 (在这种情况下,请 await 写就绪,然后再次尝试PQputCopyEnd调用.)如果发生硬错误,则返回-1;否则,返回-1.您可以使用PQerrorMessage来检索详细信息。

成功调用PQputCopyEnd后,调用PQgetResult以获取COPY命令的最终结果状态。您可以 await 这种结果以通常的方式出现。然后返回正常操作。

34 .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释放非NULL结果缓冲区。

成功返回一行后,返回值就是该行中的数据字节数(该数字将始终大于零)。返回的字符串始终以 null 终止,尽管这可能仅对文本COPY有用。结果为零表示COPY仍在进行中,但尚无行可用(仅当* async *为 true 时才有可能)。结果为-1 表示COPY已完成。结果为-2 表示发生了错误(原因为PQerrorMessage)。

如果* async 为真(不为零),则PQgetCopyData将不会阻止 awaitImporting。如果COPY仍在进行但没有完整的行可用,它将返回零。 (在这种情况下,请 await 就绪,然后再调用PQconsumeInput,然后再次调用PQgetCopyData.) * async *为假(零)时,PQgetCopyData将阻塞直到数据可用或操作完成。

PQgetCopyData返回-1 之后,调用PQgetResult以获取COPY命令的最终结果状态。您可以 await 这种结果以通常的方式出现。然后返回正常操作。

34 .9.3. COPY 的过时功能

这些函数表示处理COPY的较旧方法。尽管它们仍然可以使用,但由于错误处理能力差,检测数据结束的方法不方便以及缺乏对二进制或非阻塞传输的支持,因此不建议使用它们。

  • PQgetline
    • 将以换行符结尾的字符行(由服务器传输)读取到大小为* length *的缓冲区字符串中。
int PQgetline(PGconn *conn,
              char *buffer,
              int length);

此函数最多将* length * -1 个字符复制到缓冲区中,并将终止换行符转换为零字节。 PQgetline在 Importing 末尾返回EOF;如果已读取整行,则返回 0;如果缓冲区已满但尚未读取终止换行符,则返回 1.

请注意,应用程序必须检查新行是否包含两个字符\.,这表示服务器已完成发送COPY命令的结果。如果应用程序接收的行长度可能超过* length * -1 个字符,则需要确保其正确识别\.行(例如,不要将长数据行的结尾误认为是终止符行) )。

  • PQgetlineAsync
    • 将一行COPY数据(由服务器传输)读入缓冲区而不会阻塞。
int PQgetlineAsync(PGconn *conn,
                   char *buffer,
                   int bufsize);

此功能类似于PQgetline,但必须异步读取COPY数据(即无阻塞)的应用程序可以使用它。发出COPY命令并获得PGRES_COPY_OUT响应后,应用程序应调用PQconsumeInputPQgetlineAsync,直到检测到数据结束 signal 为止。

PQgetline不同,此功能负责检测数据结束。

在每次调用中,如果 libpq 的 Importing 缓冲区中有完整的数据行,则PQgetlineAsync将返回数据。否则,直到该行的其余部分到达之前,不会返回任何数据。如果已识别出复制结束标记,则函数返回-1;如果没有可用数据,则返回 0;或者给出一个正数,表示返回的数据字节数。如果返回-1,则调用者必须接下来调用PQendcopy,然后返回到正常处理。

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

  • PQputline
    • 将以空值结尾的字符串发送到服务器。如果确定,则返回 0,如果无法发送该字符串,则返回EOF
int PQputline(PGconn *conn,
              const char *string);

一系列对PQputline的调用所发送的COPY数据流与PQgetlineAsync返回的格式相同,不同之处在于应用程序不必每次PQputline都发送一个数据行。每次呼叫可以发送部分线路或多条线路。

Note

在 PostgreSQL 协议 3.0 之前,应用程序必须显式发送两个字符\.作为最后一行,以向服务器表明它已完成COPY数据的发送。尽管此方法仍然有效,但已弃用,并且可以预期在将来的版本中删除\.的特殊含义。发送实际数据后,调用PQendcopy就足够了。

  • PQputnbytes
    • 将非空终止的字符串发送到服务器。如果确定,则返回 0,如果无法发送该字符串,则返回EOF
int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);

完全类似于PQputline,不同之处在于数据缓冲区不必为空值终止,因为要直接指定要发送的字节数。发送二进制数据时,请使用此过程。

  • PQendcopy
    • 与服务器同步。
int PQendcopy(PGconn *conn);

此功能将 await 服务器完成复制。当使用PQputline将最后一个字符串发送到服务器时,或者使用PQgetline从服务器收到了最后一个字符串时,应该发出该消息。它必须发出,否则服务器将与 Client 端“不同步”。从此函数返回后,服务器已准备好接收下一条 SQL 命令。成功完成时,返回值为 0,否则返回非零。 (如果返回值非零,则使用PQerrorMessage来检索详细信息.)

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

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