# 34.4.异步命令处理
这个PQexec
该函数足以在正常的同步应用程序中提交命令。然而,它有一些对某些用户很重要的缺陷:
PQexec
等待命令完成。应用程序可能还有其他工作要做(比如维护用户界面),在这种情况下,它不想阻止等待响应。由于客户端应用程序在等待结果时暂停执行,因此应用程序很难决定是否要尝试取消正在执行的命令。(这可以通过信号处理器完成,但不能通过其他方式完成。)
PQexec
只能返回一个PGresult
结构如果提交的命令字符串包含多个SQL命令,则除最后一个命令外PGresult
被丢弃PQexec
.PQexec
始终收集命令的整个结果,并将其缓冲在单个PGresult
。虽然这简化了应用程序的错误处理逻辑,但对于包含多行的结果来说可能不切实际。不喜欢这些限制的应用程序可以使用
PQexec
它由以下部分构成:PQsendQuery
和PQgetResult
.还有PQsendQueryParams
,PQsendPrepare
,PQsendQueryPrepared
,PQsendDescribePrepared
, 和PQsendDescribePortal
, 它可以与PQgetResult
复制的功能PQexec 参数
,PQprepare
,PQexec 准备
,PQdescribePrepared
, 和PQdescribePortal
分别。
向服务器提交命令而不等待结果。如果命令成功发送则返回 1,否则返回 0(在这种情况下,使用PQerrorMessage
以获取有关故障的更多信息)。
int PQsendQuery(PGconn *conn, const char *command);
调用成功后PQsendQuery
, 称呼PQgetResult
一次或多次获得结果。PQsendQuery
不能再次调用(在同一连接上),直到PQgetResult
返回了一个空指针,表示命令执行完毕。
在管道模式下,不允许包含多个 SQL 命令的命令字符串。
向服务器提交命令和单独的参数,而不等待结果。
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 参数
,它只允许查询字符串中有一个命令。
发送请求以使用给定参数创建准备好的语句,而无需等待完成。
int PQsendPrepare(PGconn *conn,
const char *stmtName,
const char *query,
int nParams,
const Oid *paramTypes);
这是一个异步版本PQprepare
:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult
判断服务器是否成功创建了prepared statement。该函数的参数的处理方式与PQprepare
.
发送请求以执行具有给定参数的准备好的语句,而无需等待结果。
int PQsendQueryPrepared(PGconn *conn,
const char *stmtName,
int nParams,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);
这类似于PQsendQueryParams
,但要执行的命令是通过命名先前准备的语句来指定的,而不是给出查询字符串。该函数的参数的处理方式与PQexec 准备
.
提交请求以获取有关指定预准备语句的信息,而无需等待完成。
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
这是一个异步版本PQdescribePrepared
:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult
来获得结果。该函数的参数的处理方式与PQdescribePrepared
.
提交请求以获取有关指定门户的信息,而无需等待完成。
int PQsendDescribePortal(PGconn *conn, const char *portalName);
这是一个异步版本PQdescribePortal
:如果能够发送请求,则返回 1,否则返回 0.调用成功后,调用PQgetResult
来获得结果。该函数的参数的处理方式与PQdescribePortal
.
等待前一个结果的下一个结果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 完全处理错误信息。
使用PQsendQuery
和PQgetResult
解决其中之一执行程序
的问题:如果一个命令字符串包含多个SQL命令,则可以单独获取这些命令的结果。(顺便说一下,这允许一种简单形式的重叠处理:客户端可以处理一个命令的结果,而服务器仍在处理同一命令字符串中的后续查询。)
另一个经常需要的功能,可以通过PQsendQuery
和PQgetResult
一次检索大查询结果一行。这在第 34.6 节.
本身,调用PQgetResult
仍然会导致客户端阻塞,直到服务器完成下一个 SQL 命令。这可以通过正确使用另外两个功能来避免:
如果可以从服务器获得输入,则使用它。
int PQconsumeInput(PGconn *conn);
PQconsume输入
通常返回 1 表示“没有错误”,但如果出现某种故障则返回 0(在这种情况下PQerrorMessage
可以咨询)。请注意,结果并未说明是否实际收集了任何输入数据。打电话后PQconsume输入
,应用程序可以检查PQis忙碌
和/或PQ 通知
看看他们的状态是否发生了变化。
PQconsume输入
即使应用程序还没有准备好处理结果或通知,也可以调用。该函数将读取可用数据并将其保存在缓冲区中,从而导致选择()
阅读就绪指示离开。该应用程序因此可以使用PQconsume输入
清除选择()
立即状况,然后在闲暇时检查结果。
如果命令忙,则返回 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命令或数据值,就会发生这种情况。(如果应用程序通过抄送
(然而)为了防止这种可能性并实现完全无阻塞的数据库操作,可以使用以下附加功能。
设置连接的非阻塞状态。
int PQsetnonblocking(PGconn *conn, int arg);
如果需要,将连接状态设置为非阻塞*阿格
是1,如果阿格
*是0.如果正常,则返回0;如果错误,则返回1.
在非阻塞状态下,调用PQsendQuery
,PQputline
,PQputnbytes
,PQputCopyData
和PQendcopy
如果需要再次调用,则不会阻止,而是返回错误。
注意PQexec
不支持非阻塞模式;如果它被调用,它将以阻塞方式运行。
返回数据库连接的阻塞状态。
int PQisnonblocking(const PGconn *conn);
如果连接设置为非阻塞模式,则返回1;如果连接设置为阻塞模式,则返回0.
尝试将任何排队的输出数据刷新到服务器。如果成功(或如果发送队列为空),则返回0;如果由于某种原因失败,则返回-1;如果无法发送发送队列中的所有数据,则返回1(这种情况仅在连接未阻塞时发生)。
int PQflush(PGconn *conn);
在非阻塞连接上发送任何命令或数据后,调用PQflush
.如果返回1,则等待套接字变为读写就绪。如果已准备好写入,请致电PQflush
再一次如果它已准备就绪,请致电pqconsumer输入
,然后打电话PQflush
再一次重复直到PQflush
返回0.(有必要检查read ready(读取准备就绪)并使用pqconsumer输入
,因为服务器可以阻止向我们发送数据,例如通知消息,并且在我们读取数据之前不会读取我们的数据。)一旦PQflush
返回0,等待套接字读取就绪,然后如上所述读取响应。