# 34.4.异步命令处理

这个PQexec该函数足以在正常的同步应用程序中提交命令。然而,它有一些对某些用户很重要的缺陷:

PQsendQuery

向服务器提交命令而不等待结果。如果命令成功发送则返回 1,否则返回 0(在这种情况下,使用PQerrorMessage以获取有关故障的更多信息)。

int PQsendQuery(PGconn *conn, const char *command);

调用成功后PQsendQuery, 称呼PQgetResult一次或多次获得结果。PQsendQuery不能再次调用(在同一连接上),直到PQgetResult返回了一个空指针,表示命令执行完毕。

在管道模式下,不允许包含多个 SQL 命令的命令字符串。

PQsendQueryParams

向服务器提交命令和单独的参数,而不等待结果。

int PQsendQueryParams(PGconn *conn,
                      const char *command,
                      int nParams,
                      const Oid *paramTypes,
                      const char * const *paramValues,
                      const int *paramLengths,
                      const int *paramFormats,
                      int resultFormat);

这相当于PQsendQuery除了查询参数可以与查询字符串分开指定。该函数的参数的处理方式与PQexec 参数.喜欢PQexec 参数,它只允许查询字符串中有一个命令。

PQsendPrepare

发送请求以使用给定参数创建准备好的语句,而无需等待完成。

int PQsendPrepare(PGconn *conn,
                  const char *stmtName,
                  const char *query,
                  int nParams,
                  const Oid *paramTypes);

这是一个异步版本PQprepare:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult判断服务器是否成功创建了prepared statement。该函数的参数的处理方式与PQprepare.

PQsendQueryPrepared

发送请求以执行具有给定参数的准备好的语句,而无需等待结果。

int PQsendQueryPrepared(PGconn *conn,
                        const char *stmtName,
                        int nParams,
                        const char * const *paramValues,
                        const int *paramLengths,
                        const int *paramFormats,
                        int resultFormat);

这类似于PQsendQueryParams,但要执行的命令是通过命名先前准备的语句来指定的,而不是给出查询字符串。该函数的参数的处理方式与PQexec 准备.

PQsendDescribePrepared

提交请求以获取有关指定预准备语句的信息,而无需等待完成。

int PQsendDescribePrepared(PGconn *conn, const char *stmtName);

这是一个异步版本PQdescribePrepared:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult来获得结果。该函数的参数的处理方式与PQdescribePrepared.

PQsendDescribePortal

提交请求以获取有关指定门户的信息,而无需等待完成。

int PQsendDescribePortal(PGconn *conn, const char *portalName);

这是一个异步版本PQdescribePortal:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult来获得结果。该函数的参数的处理方式与PQdescribePortal.

PQgetResult

等待前一个结果的下一个结果PQsendQuery,PQsendQueryParams,PQsendPrepare,PQsendQueryPrepared,PQsendDescribePrepared,PQsendDescribePortal, 或者PQpipelineSync调用,并返回它。命令完成时返回一个空指针,不会再有结果。

PGresult *PQgetResult(PGconn *conn);

PQgetResult必须重复调用,直到它返回一个空指针,表示命令完成。(如果在没有命令处于活动状态时调用,PQgetResult将立即返回一个空指针。)每个非空结果来自PQgetResult应该使用相同的处理PG结果前面描述的访问器函数。不要忘记释放每个结果对象PQclear完成后。注意PQgetResult仅当命令处于活动状态且必要的响应数据尚未被读取时才会阻塞PQconsume输入.

在流水线模式下,PQgetResult除非发生错误,否则将正常返回;对于在导致错误的查询之后发送的任何后续查询,直到(并且不包括)下一个同步点,类型的特殊结果PGRES_PIPELINE_ABORTED将被返回,并在其后返回一个空指针。当达到管道同步点时,类型为PGRES_PIPELINE_SYNC将被退回。紧跟在同步点之后的下一个查询的结果(即同步点之后不返回空指针)。

# 笔记

即使当PQresult状态表示致命错误,PQgetResult应该调用直到它返回一个空指针,以允许 libpq 完全处理错误信息。

使用PQsendQueryPQgetResult解决其中之一执行程序的问题:如果一个命令字符串包含多个SQL命令,则可以单独获取这些命令的结果。(顺便说一下,这允许一种简单形式的重叠处理:客户端可以处理一个命令的结果,而服务器仍在处理同一命令字符串中的后续查询。)

另一个经常需要的功能,可以通过PQsendQueryPQgetResult一次检索大查询结果一行。这在第 34.6 节.

本身,调用PQgetResult仍然会导致客户端阻塞,直到服务器完成下一个 SQL 命令。这可以通过正确使用另外两个功能来避免:

PQconsume输入

如果可以从服务器获得输入,则使用它。

int PQconsumeInput(PGconn *conn);

PQconsume输入通常返回 1 表示“没有错误”,但如果出现某种故障则返回 0(在这种情况下PQerrorMessage可以咨询)。请注意,结果并未说明是否实际收集了任何输入数据。打电话后PQconsume输入,应用程序可以检查PQis忙碌和/或PQ 通知看看他们的状态是否发生了变化。

PQconsume输入即使应用程序还没有准备好处理结果或通知,也可以调用。该函数将读取可用数据并将其保存在缓冲区中,从而导致选择()阅读就绪指示离开。该应用程序因此可以使用PQconsume输入清除选择()立即状况,然后在闲暇时检查结果。

PQis忙碌

如果命令忙,则返回 1,即PQgetResult会阻塞等待输入。返回 0 表示PQgetResult可以在保证不阻塞的情况下调用。

int PQisBusy(PGconn *conn);

PQis忙碌本身不会尝试从服务器读取数据;所以PQconsume输入必须先调用,否则忙碌状态永远不会结束。

使用这些函数的典型应用程序将有一个主循环,它使用选择()要么轮询()等待它必须响应的所有条件。条件之一将从服务器输入,根据选择()表示文件描述符上的可读数据PQsocket.当主循环检测到输入就绪时,它应该调用PQconsume输入读取输入。然后它可以调用PQis忙碌, 其次是PQgetResult如果PQis忙碌返回假 (0)。它也可以调用PQ 通知检测通知消息(见第 34.9 节)。

使用的客户端PQsendQuery/PQgetResult还可以尝试取消服务器仍在处理的命令;看第 34.7 节.但不管取消,应用程序必须使用PQgetResult。成功取消只会导致命令提前终止。

通过使用上述功能,可以避免在等待来自数据库服务器的输入时发生阻塞。但是,应用程序仍有可能阻止等待向服务器发送输出。这种情况相对少见,但如果发送很长的SQL命令或数据值,就会发生这种情况。(如果应用程序通过抄送(然而)为了防止这种可能性并实现完全无阻塞的数据库操作,可以使用以下附加功能。

PQsetnonblocking

设置连接的非阻塞状态。

int PQsetnonblocking(PGconn *conn, int arg);

如果需要,将连接状态设置为非阻塞*阿格是1,如果阿格*是0.如果正常,则返回0;如果错误,则返回1.

在非阻塞状态下,调用PQsendQuery,PQputline,PQputnbytes,PQputCopyDataPQendcopy如果需要再次调用,则不会阻止,而是返回错误。

注意PQexec不支持非阻塞模式;如果它被调用,它将以阻塞方式运行。

PQIS非阻塞

返回数据库连接的阻塞状态。

int PQisnonblocking(const PGconn *conn);

如果连接设置为非阻塞模式,则返回1;如果连接设置为阻塞模式,则返回0.

PQflush

尝试将任何排队的输出数据刷新到服务器。如果成功(或如果发送队列为空),则返回0;如果由于某种原因失败,则返回-1;如果无法发送发送队列中的所有数据,则返回1(这种情况仅在连接未阻塞时发生)。

int PQflush(PGconn *conn);

在非阻塞连接上发送任何命令或数据后,调用PQflush.如果返回1,则等待套接字变为读写就绪。如果已准备好写入,请致电PQflush再一次如果它已准备就绪,请致电pqconsumer输入,然后打电话PQflush再一次重复直到PQflush返回0.(有必要检查read ready(读取准备就绪)并使用pqconsumer输入,因为服务器可以阻止向我们发送数据,例如通知消息,并且在我们读取数据之前不会读取我们的数据。)一旦PQflush返回0,等待套接字读取就绪,然后如上所述读取响应。