On this page
33.9. 与 COPY 命令关联的功能
PostgreSQL 中的COPY
命令具有读取或写入 libpq 使用的网络连接的选项。本节中描述的功能允许应用程序通过提供或使用复制的数据来利用此功能。
整个过程是应用程序首先通过PQexec
或等效功能之一发出 SQL COPY
命令。对此的响应(如果命令中没有错误)将是一个PGresult
对象,其状态码为PGRES_COPY_OUT
或PGRES_COPY_IN
(取决于指定的复制方向)。然后,应用程序应使用本节的功能来接收或发送数据行。数据传输完成后,将返回另一个PGresult
对象以指示传输成功或失败。它的状态为PGRES_COMMAND_OK
(表示成功)或PGRES_FATAL_ERROR
(如果遇到问题)。此时,可以通过PQexec
发出其他 SQL 命令。 (在进行COPY
操作时,无法使用同一连接执行其他 SQL 命令.)
如果通过PQexec
在可能包含其他命令的字符串中发出了COPY
命令,则应用程序必须在完成COPY
序列后 continue 通过PQgetResult
提取结果。仅当PQgetResult
返回NULL
时,才能确定PQexec
命令字符串已完成,并且可以发出更多命令。
只有从PQexec
或PQgetResult
获得结果状态为PGRES_COPY_OUT
或PGRES_COPY_IN
后,才应执行本节的功能。
带有这些状态值之一的PGresult
对象携带一些有关正在启动的COPY
操作的附加数据。这些附加数据可通过与查询结果结合使用的功能获得:
-
- 返回要复制的列(字段)数。
-
- 0 表示整体复制格式为文本格式(行由换行符分隔,列由分隔符分隔,等等)。 1 表示整体复制格式为二进制。有关更多信息,请参见COPY。
-
- 返回与复制操作的每一列关联的格式代码(0 表示文本,1 表示二进制)。当整体复制格式为文本时,每列格式代码将始终为零,但二进制格式可以同时支持文本列和二进制列。 (但是,从当前的
COPY
实现开始,二进制副本中仅显示二进制列;因此,当前每列格式始终与总体格式匹配.)
- 返回与复制操作的每一列关联的格式代码(0 表示文本,1 表示二进制)。当整体复制格式为文本时,每列格式代码将始终为零,但二进制格式可以同时支持文本列和二进制列。 (但是,从当前的
Note
这些附加数据值仅在使用协议 3.0 时可用。使用协议 2.0 时,所有这些函数将返回 0.
33 .9.1. 发送 COPY 数据的功能
这些功能用于在COPY FROM STDIN
期间发送数据。如果在连接未处于COPY_IN
状态时调用它们,它们将失败。
int PQputCopyData(PGconn *conn,
const char *buffer,
int nbytes);
将指定的* buffer
*中长度为nbytes
*的COPY
数据传输到服务器。如果数据已排队,则结果为 1;如果由于缓冲区已满而未排队,则结果为 0(这只会在非阻塞模式下发生);如果发生错误,则结果为-1. (如果返回值为-1,请使用PQerrorMessage
来检索详细信息.如果该值为零,请 await 写就绪,然后重试.)
应用程序可以将COPY
数据流划分为任何方便大小的缓冲区负载。发送时,缓冲区加载边界没有语义意义。数据流的内容必须与COPY
命令所需的数据格式匹配;有关详情,请参见COPY。
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 这种结果以通常的方式出现。然后返回正常操作。
33 .9.2. 接收 COPY 数据的功能
这些功能用于在COPY TO STDOUT
期间接收数据。如果在连接未处于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 这种结果以通常的方式出现。然后返回正常操作。
33 .9.3. COPY 的过时功能
这些函数表示处理COPY
的较旧方法。尽管它们仍然可以使用,但由于错误处理能力差,检测数据结束的方法不方便以及缺乏对二进制或非阻塞传输的支持,因此不建议使用它们。
int PQgetline(PGconn *conn,
char *buffer,
int length);
此函数最多将* length
* -1 个字符复制到缓冲区中,并将终止换行符转换为零字节。 PQgetline
在 Importing 末尾返回EOF
;如果已读取整行,则返回 0;如果缓冲区已满但尚未读取终止换行符,则返回 1.
请注意,应用程序必须检查新行是否包含两个字符\.
,这表示服务器已完成发送COPY
命令的结果。如果应用程序接收的行长度可能超过* length
* -1 个字符,则需要确保其正确识别\.
行(例如,不要将长数据行的结尾误认为是终止符行) )。
int PQgetlineAsync(PGconn *conn,
char *buffer,
int bufsize);
此功能类似于PQgetline
,但必须异步读取COPY
数据(即无阻塞)的应用程序可以使用它。发出COPY
命令并获得PGRES_COPY_OUT
响应后,应用程序应调用PQconsumeInput
和PQgetlineAsync
,直到检测到数据结束 signal 为止。
与PQgetline
不同,此功能负责检测数据结束。
在每次调用中,如果 libpq 的 Importing 缓冲区中有完整的数据行,则PQgetlineAsync
将返回数据。否则,直到该行的其余部分到达之前,不会返回任何数据。如果已识别出复制结束标记,则函数返回-1;如果没有可用数据,则返回 0;或者给出一个正数,表示返回的数据字节数。如果返回-1,则调用者必须接下来调用PQendcopy
,然后返回到正常处理。
返回的数据不会超出数据行边界。如果可能,将一次返回整行。但是,如果调用者提供的缓冲区太小而无法容纳服务器发送的行,则将返回部分数据行。对于文本数据,可以通过测试最后返回的字节是否为\n
来检测。 (在二进制COPY
中,需要对COPY
数据格式进行实际解析才能做出等效确定.)返回的字符串不是以空值结尾的。 (如果要添加终止 null,请确保传递比实际可用空间小* bufsize
*.)
int PQputline(PGconn *conn,
const char *string);
一系列对PQputline
的调用所发送的COPY
数据流与PQgetlineAsync
返回的格式相同,不同之处在于应用程序不必每次PQputline
都发送一个数据行。每次呼叫可以发送部分线路或多条线路。
Note
在 PostgreSQL 协议 3.0 之前,应用程序必须显式发送两个字符\.
作为最后一行,以向服务器表明它已完成COPY
数据的发送。尽管此方法仍然有效,但已弃用,并且可以预期在将来的版本中删除\.
的特殊含义。发送实际数据后,调用PQendcopy
就足够了。
int PQputnbytes(PGconn *conn,
const char *buffer,
int nbytes);
完全类似于PQputline
,不同之处在于数据缓冲区不必为空值终止,因为要直接指定要发送的字节数。发送二进制数据时,请使用此过程。
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 命令时,此命令才能正常工作。