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 实现,在二进制拷贝里只出现二进制字段; 所以目前每字段的格式总是匹配纵起格式。)
注意: 这些额外的数据值只能在使用 3.0 版本的协议的时候获得。在使用 2.0 版本的协议时,所有这些函数都返回 0。
这些函数用于在 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 命令的最终结果状态。
我们可以用平常的方法来等待这个结果可用。然后返回到正常的操作。
这些函数用于在 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 命令的最后结果状态。
我们可以用通常的方法等待这个结果可用。然后返回到正常操作。
下面的这些函数代表了以前的处理 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
返回 NULL。
类似地,
PGRES_COPY_IN
结果是用一系列 PQputline
调用最后跟着
PQendcopy
,然后返回到 PQgetResult
循环。
这样的排列将保证嵌入到一系列 SQL
命令里的 COPY 命令将被正确执行。
旧的应用大多通过
PQexec
提交一个 COPY 命令并且假设在
PQendcopy
后事务完成。
这样只有在 COPY 是命令字串里的唯一的
SQL 命令时才能正确工作。