PQexec
函数足以在普通的同步应用程序中提交命令。然而,它有一些缺陷,这些缺陷对某些用户来说可能很重要
不喜欢这些限制的应用程序可以改为使用 PQexec
构建的基础函数:PQsendQuery
和 PQgetResult
。还有 PQsendQueryParams
、PQsendPrepare
、PQsendQueryPrepared
、PQsendDescribePrepared
、PQsendDescribePortal
、PQsendClosePrepared
和 PQsendClosePortal
,它们可以与 PQgetResult
一起使用,以复制 PQexecParams
、PQprepare
、PQexecPrepared
、PQdescribePrepared
、PQdescribePortal
、PQclosePrepared
和 PQclosePortal
的功能。
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
#等待先前 PQsendQuery
, PQsendQueryParams
, PQsendPrepare
, PQsendQueryPrepared
, PQsendDescribePrepared
, PQsendDescribePortal
, PQsendClosePrepared
, PQsendClosePortal
, PQsendPipelineSync
, 或 PQpipelineSync
调用的下一个结果,并返回它。当命令完成并且不会有更多结果时,将返回空指针。
PGresult *PQgetResult(PGconn *conn);
必须重复调用 PQgetResult
,直到它返回空指针,表示命令已完成。(如果在没有活动命令时调用,PQgetResult
将立即返回空指针。)应使用之前描述的相同 PGresult
访问器函数处理来自 PQgetResult
的每个非空结果。不要忘记在使用完每个结果对象后使用 PQclear
释放它。请注意,只有当命令处于活动状态并且 PQconsumeInput
尚未读取必要的响应数据时,PQgetResult
才会阻塞。
在管道模式下,除非发生错误,否则 PQgetResult
将正常返回;对于在导致错误的查询之后发送的任何后续查询,直到(但不包括)下一个同步点,将返回类型为 PGRES_PIPELINE_ABORTED
的特殊结果,之后将返回空指针。当到达管道同步点时,将返回类型为 PGRES_PIPELINE_SYNC
的结果。同步点之后的下一个查询的结果将紧随其后(即,同步点之后不会返回空指针)。
即使 PQresultStatus
指示致命错误,也应调用 PQgetResult
直到它返回空指针,以便 libpq 完全处理错误信息。
使用 PQsendQuery
和 PQgetResult
可以解决 PQexec
的一个问题:如果一个命令字符串包含多个SQL命令,则可以单独获取这些命令的结果。(顺便说一下,这允许一种简单的重叠处理形式:客户端可以在服务器仍在处理同一命令字符串中稍后的查询时处理一个命令的结果。)
使用 PQsendQuery
和 PQgetResult
可以获得的另一个常用功能是一次检索有限行的大型查询结果。这在第 32.6 节中讨论。
单独调用 PQgetResult
仍然会导致客户端阻塞,直到服务器完成下一个SQL命令。通过正确使用另外两个函数可以避免这种情况
PQconsumeInput
#如果有来自服务器的输入,则使用它。
int PQconsumeInput(PGconn *conn);
PQconsumeInput
通常返回 1,表示 “无错误”,但如果出现某种问题则返回 0(在这种情况下,可以查阅 PQerrorMessage
)。请注意,结果并未说明是否实际收集了任何输入数据。调用 PQconsumeInput
后,应用程序可以检查 PQisBusy
和/或 PQnotifies
以查看其状态是否已更改。
即使应用程序尚未准备好处理结果或通知,也可以调用 PQconsumeInput
。该函数将读取可用数据并将其保存在缓冲区中,从而使 select()
的就绪读取指示消失。因此,应用程序可以使用 PQconsumeInput
立即清除 select()
条件,然后从容检查结果。
PQisBusy
#如果命令繁忙,则返回 1,即 PQgetResult
将阻塞等待输入。返回 0 表示可以保证在不阻塞的情况下调用 PQgetResult
。
int PQisBusy(PGconn *conn);
PQisBusy
本身不会尝试从服务器读取数据;因此,必须首先调用 PQconsumeInput
,否则繁忙状态将永远不会结束。
使用这些函数的典型应用程序将有一个主循环,该循环使用 select()
或 poll()
来等待它必须响应的所有条件。其中一个条件是来自服务器的可用输入,就 select()
而言,这意味着 PQsocket
标识的文件描述符上的可读数据。当主循环检测到输入就绪时,它应调用 PQconsumeInput
来读取输入。然后,它可以调用 PQisBusy
,如果 PQisBusy
返回 false (0),则调用 PQgetResult
。它还可以调用 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。
在非阻塞状态下,成功调用 PQsendQuery
、PQputline
、PQputnbytes
、PQputCopyData
和 PQendcopy
不会阻塞;它们的更改会存储在本地输出缓冲区中,直到刷新为止。不成功的调用将返回错误,并且必须重试。
请注意,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,请等待套接字变为可读状态,然后如上所述读取响应。
如果您发现文档中的任何内容不正确,与您使用特定功能的经验不符,或需要进一步澄清,请使用此表单报告文档问题。