Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

32.4.异步命令处理 #

PQexec 函数足够在普通、同步应用程序中提交命令。不过,它有几个缺陷,对于有些用户来说很重要

不喜欢这些限制的应用程序可以使用构成 PQexec 的底层函数:PQsendQueryPQgetResult。还有 PQsendQueryParamsPQsendPreparePQsendQueryPreparedPQsendDescribePreparedPQsendDescribePortalPQsendClosePreparedPQsendClosePortal,它们可与 PQgetResult 一起用于复制 PQexecParamsPQpreparePQexecPreparedPQdescribePreparedPQdescribePortal PQclosePreparedPQclosePortal 的功能,分别。

PQsendQuery #

向服务器提交一条命令而不等待结果。如果成功分派命令,则返回 1;如果未成功分派命令,则返回 0(在这种情况下,使用 PQerrorMessage 获取有关该失败的更多信息)。

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

在成功调用 PQsendQuery 之后,调用 PQgetResult 一次或多次以获取结果。在 PQgetResult 返回一个空指针(表示命令已完成)之前,不能再次调用 PQsendQuery(对同一连接)。

在管道模式下,不允许使用该函数。

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,除了查询参数可以与查询字符串分开指定。该函数的参数与 PQexecParams 的处理方式相同。与 PQexecParams 一样,它只允许在查询字符串中使用一个命令。

PQsendPrepare #

在不等待完成的情况下,发送一个请求以使用给定的参数创建预处理语句。

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

这是 PQprepare 的异步版本:如果能够调度请求,则返回 1;否则返回 0。调用成功后,调用 PQgetResult 以确定服务器是否成功创建了预处理语句。该函数的参数与 PQprepare 的处理方式相同。

PQsendQueryPrepared #

在不等待结果的情况下,发送一个请求以使用给定的参数执行预处理语句。

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

这类似于 PQsendQueryParams,但是要执行的命令是通过命名一个以前准备好的语句来指定的,而不是提供一个查询字符串。该函数的参数与 PQexecPrepared 的处理方式相同。

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相同。

PQsendClosePrepared #

提交一个在未等待完成的情况下关闭指定已准备语句的请求。

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

这是PQclosePrepared的异步版本:如果它能够分派请求,则返回 1;如果没有,则返回 0。在成功的调用之后,请调用PQgetResult以获取结果。该函数的参数处理方式与PQclosePrepared相同。

PQsendClosePortal #

提交一个在未等待完成的情况下关闭指定门户的请求。

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

这是PQclosePortal的异步版本:如果它能够分派请求,则返回 1;如果没有,则返回 0。在成功的调用之后,请调用PQgetResult以获取结果。该函数的参数处理方式与PQclosePortal相同。

PQgetResult #

等待来自先前PQsendQueryPQsendQueryParamsPQsendPreparePQsendQueryPreparedPQsendDescribePreparedPQsendDescribePortalPQsendClosePreparedPQsendClosePortalPQsendPipelineSyncPQpipelineSync调用的下一个结果并返回该结果。当命令完成并且不再有更多结果时,返回一个空指针。

PGresult *PQgetResult(PGconn *conn);

PQgetResult 必须反复调用,直到其返回空指针,表明命令已完成。(如果在没有激活命令时进行调用,PQgetResult 将只立即返回一个空指针。)PQgetResult 的每一个非空结果应使用前面已述的相同的 PGresult 访问器函数进行处理。用完后,不要忘记使用 PQclear 释放每个结果对象。请注意,PQgetResult 仅当一个命令已激活且必要的响应数据尚未被 PQconsumeInput 读取时才会阻塞。

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

注意

即使 PQresultStatus 表明存在致命错误,也应调用 PQgetResult,直到其返回空指针,以允许 libpq 完全处理错误信息。

使用 PQsendQueryPQgetResult 可以解决 PQexec 的一个问题:如果一个命令字符串包含多个SQL命令,则可以分别获得这些命令的结果。(顺便说一下,这允许多种形式的重叠处理:当服务器仍在处理同一命令字符串中的后续查询时,客户端可以处理一个命令的结果。)

另一个经常需要通过 PQsendQueryPQgetResult 实现的功能是可以一次最多检索限量行大查询结果。这在第 32.6 节中讨论。

单独调用 PQgetResult 仍将导致客户端阻塞,直到服务器完成下一个SQL命令。这可以通过适当使用另外两个函数来避免

PQconsumeInput #

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

int PQconsumeInput(PGconn *conn);

PQconsumeInput 通常返回 1 表示 无错误,但如果出现某种麻烦(在这种情况下,可以查阅 PQerrorMessage),则返回 0。请注意,此结果不会提示是否实际收集到任何输入数据。调用 PQconsumeInput 后,应用程序可以检查 PQisBusy 和/或 PQnotifies 以查看其状态是否已更改。

即使应用程序尚未准备好处理结果或通知,也可以调用 PQconsumeInput。此函数将读取可用数据并将其保存在缓冲区中,从而导致消除 select() 已读就绪指示。因此应用程序可以使用 PQconsumeInput 立即清除 select() 条件,然后从容检查结果。

PQisBusy #

如果某个命令正忙,即 PQgetResult 将因等待输入而被阻塞,则返回 1。返回 0 表示可以保证调用 PQgetResult 时不进行阻塞。

int PQisBusy(PGconn *conn);

PQisBusy 本身不会尝试从服务器读取数据;因此,必须首先调用 PQconsumeInput,否则忙状态将永远不会结束。

使用这些功能的典型应用程序将具有一个主循环,该主循环使用 select()poll() 来等待必应响应的所有条件。其中一个条件是服务器中可获得的输入,根据 select() 的规定,这意味着由 PQsocket 识别的文件描述符上的可读数据。当主循环检测到输入就绪时,它应调用 PQconsumeInput 来读取输入。然后,它可以调用 PQisBusy,随后调用 PQgetResult,如果 PQisBusy 返回假 (0) 的话。它还可以调用 PQnotifies 来检测 NOTIFY 消息(请参见 第 32.9 节)。

使用 PQsendQuery/PQgetResult 的客户端还可以尝试取消服务器仍在处理的命令;请参见 第 32.7 节。但是无论 PQcancelBlocking 的返回值如何,应用程序都必须使用 PQgetResult 继续进行正常的读取结果序列。成功的取消只会导致命令终止得比否则更快。

通过使用上述函数,可以避免在等待数据库服务器输入时阻塞。但是,应用程序仍然可能会阻塞,等待向服务器发送输出。这种情况相对不常见,但如果发送非常长的 SQL 命令或数据值,则可能会发生这种情况。(但是,如果应用程序通过 COPY IN 发送数据,则这种可能性更大。)要防止出现这种情况并实现完全非阻塞的数据库操作,可以使用以下附加函数。

PQsetnonblocking #

设置连接的非阻塞状态。

int PQsetnonblocking(PGconn *conn, int arg);

如果 arg 为 1,则将连接状态设置为非阻塞;如果 arg 为 0,则将其设置为阻塞。如果正确,则返回 0;如果出错,则返回 -1。

在非阻塞状态下,对 PQsendQueryPQputlinePQputnbytesPQputCopyDataPQendcopy 的成功调用不会阻塞;其更改会存储在本地输出缓冲区中,直至刷新为止。不成功的调用将返回错误,并且必须重试。

请注意,PQexec 不识别非阻塞模式;如果调用了此函数,无论如何它都将以阻塞模式执行。

PQisnonblocking #

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

int PQisnonblocking(const PGconn *conn);

如果连接已设置为非阻塞模式,则返回 1;如果处于阻塞状态,则返回 0。

PQflush #

尝试将所有排队的输出数据刷新到服务器。如果成功(或发送队列为空),则返回 0;如果因某些原因失败,则返回 -1;如果尚未能够发送发送队列中的所有数据,则返回 1(仅当连接为非阻塞时才会出现这种情况)。

int PQflush(PGconn *conn);

在非阻塞连接上发送任何命令或数据后,调用 PQflush。如果其返回 1,请等待套接字变成可读或可写。如果其变成可写,请再次调用 PQflush。如果其变成可读,请调用 PQconsumeInput ,然后再次调用 PQflush。重复此操作,直至 PQflush 返回 0。(有必要使用 PQconsumeInput 检查是否可读并耗尽输入,原因是服务器可能会被阻塞,导致尝试向我们发送数据(例如 NOTICE 消息),在读取其数据之前,该服务器不会读取我们的数据。)一旦 PQflush 返回 0,请等待套接字变为可读,然后按照上述说明读取响应。