PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.4. 异步命令处理

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 33.4. 异步命令处理 PQexec函数对于在普通的同步应用中提交命令是足以胜任的。不过,它的一些缺点可能对某些用户很重要: PQexec会等待命令完成。该应用可能有其他的工作要做(例如维护用户界面),这时它将不希望阻塞等待回应。

33.4. 异步命令处理

PQexec函数对于在普通的同步应用中提交命令是足以胜任的。不过,它的一些缺点可能对某些用户很重要:

  • PQexec会等待命令完成。该应用可能有其他的工作要做(例如维护用户界面),这时它将不希望阻塞等待回应。

  • 因为客户端应用的执行在它等待结果时会被挂起,对于应用来说很难决定要不要尝试取消正在进行的命令(这可以在一个信号处理器中完成,但别无他法)。

  • PQexec只能返回一个PGresult结构。如果提交的命令串包含多个SQL命令, 除了最后一个PGresult之外都会被PQexec丢弃。

  • PQexec总是收集命令的整个结果,把它缓存在一个单一的PGresult中。虽然这简化了应用的错误处理逻辑,它对于包含很多行的结果并不现实。

不想受到这些限制的应用可以改用构建PQexec的底层函数:PQsendQuery以及PQgetResult。还有 PQsendQueryParams、 PQsendPrepare、 PQsendQueryPrepared、 PQsendDescribePrepared以及PQsendDescribePortal, 它们可以与PQgetResult一起使用来分别复制PQexecParams、 PQprepare、 PQexecPrepared、 PQdescribePrepared和 PQdescribePortal的功能。

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类似,它不能在 2.0 协议的连接上工作,并且它只允许在查询字符串中有一条命令。

PQsendPrepare

发送一个请求用给定参数创建一个预备语句,而不等待完成。

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

这个函数是PQprepare的异步版本:如果它能发送这个请求,则返回 1;如果不能,则返回 0。在成功调用之后,调用PQgetResult判断服务器是否成功创建了预备语句。这个函数的参数的处理和PQprepare一样。和PQprepare类似,它不能在 2.0 协议的连接上工作。

PQsendQueryPrepared

发送一个请求用给定参数执行一个预备语句,而不等待结果。

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

这个函数与PQsendQueryParams类似,但是要执行的命令是通过一个之前已经命名的预备语句指定, 而不是一个给出的查询字符串。该函数的参数处理和PQexecPrepared一样。和PQexecPrepared类似,它不能在 2.0 协议的连接上工作。

PQsendDescribePrepared

发送一个请求获得指定的预备语句的信息,但不等待完成。

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

这个函数是PQdescribePrepared的一个异步版本:如果它能够发送请求,则返回 1;否则,返回 0。在一次成功的调用后,调用PQgetResult来得到结果。该函数的参数处理和PQdescribePrepared一样。和PQdescribePrepared类似,它不能在 2.0 协议的连接上工作。

PQsendDescribePortal

提交一个请求来获得关于指定入口的信息,但不等待完成。

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

这个函数是PQdescribePortal的一个异步版本:如果它能够发送请求,则返回 1;否则,返回 0。在一次成功的调用后,调用PQgetResult来得到结果。该函数的参数处理和PQdescribePortal一样。和PQdescribePortal类似,它不能在 2.0 协议的连接上工作。

PQgetResult

等待来自于一个之前的 PQsendQuery、 PQsendQueryParams、 PQsendPrepare、 PQsendQueryPrepared、 PQsendDescribePrepared或 PQsendDescribePortal调用的结果,并且返回它。当该命令完成并且没有更多结果时,将返回一个空指针。

PGresult *PQgetResult(PGconn *conn);

PQgetResult必须被反复调用直到它返回一个空指针,空指针表示该命令完成(如果在没有命令活动时被调用,PQgetResult将立即返回一个空指针)。每一个来自PQgetResult的非空结果应该使用之前描述的同一个PGresult访问器处理。不要忘记在处理完之后释放每一个结果对象。注意,只有一个命令是活动的并且PQconsumeInput还没有读取必要的响应数据时, PQgetResult将会阻塞。

注意

即使当PQresultStatus指出一个致命错误时,PQgetResult也应当被调用直到它返回一个空指针,以允许libpq完全处理该错误信息。

使用PQsendQueryPQgetResult解决了PQexec的一个问题:如果一个命令字符串包含多个SQL命令,这些命令的结果可以被个别地获得(顺便说一句:这样就允许一种简单的重叠处理形式, 客户端可以处理一个命令的结果,而同时服务器可以继续处理同一命令字符串中后面的查询)。

可以被PQsendQueryPQgetResult获得的另一种常常想要的特性是一次从大型结果中检索一行。这会在第 33.5 节中讨论。

如果只调用PQgetResult(不调用PQsendQuery等)将仍会导致客户端阻塞直到服务器完成下一个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返回假(0)则接着调用PQgetResult。它还可以调用PQnotifies检测NOTIFY消息(见第 33.8 节)。

一个使用PQsendQuery/PQgetResult的客户端也可以尝试取消一个正在被服务器处理的命令,见第 33.6 节。但是,不管PQcancel的返回值是什么,应用都必须继续使用PQgetResult进行正常的结果读取序列。一次成功的取消只会导致命令比不取消时更快终止。

通过使用上述函数,我们可以避免在等待来自数据库服务器的输入时被阻塞。不过,在应用发送输出给服务器时还是可能出现阻塞。这种情况比较少见,但是如果发送非常长的 SQL 命令或者数据值时确实可能发生(不过,最有可能是在应用通过COPY IN发送数据时)。为了避免这种可能性并且实现完全地非阻塞数据库操作,可以使用下列附加函数。

PQsetnonblocking

把连接的状态设置为非阻塞。

int PQsetnonblocking(PGconn *conn, int arg);

如果arg为 1,把连接状态设置为非阻塞;如果arg为 0,把连接状态设置为阻塞。如果 OK 返回 0,如果错误返回 -1。

在非阻塞状态,调用 PQsendQueryPQputline、 PQputnbytesPQputCopyData和 PQendcopy将不会阻塞,但是如果它们需要被再次调用则会返回一个错误。

注意PQexec不会遵循任何非阻塞模式;如果调用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,应等待套接字变成读准备好并且接着按照上文所述 读取响应。

本文转自PostgreSQL中文社区,原文链接:33.4. 异步命令处理

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
1月前
|
Oracle 关系型数据库 分布式数据库
PolarDB常见问题之PolarDB(Oracle兼容版) 执行命令报错如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
6月前
|
关系型数据库 开发工具 C语言
PostgreSQL libpq开发入门
简单入门C语言开发基于PostgreSQL libpq应用
104 0
|
4月前
|
关系型数据库 数据库 PostgreSQL
postgresql|【基于pg_basebackup命令的归档备份和恢复---热备冷恢复方式】
postgresql|【基于pg_basebackup命令的归档备份和恢复---热备冷恢复方式】
50 0
|
2月前
|
SQL 存储 缓存
PostgreSQL函数管理接口
学习PostgreSQL服务端开发必须要对函数管理接口有比较深入的了解
143 0
|
2月前
|
关系型数据库 分布式数据库 PolarDB
PolarDB for PostgreSQL下载问题之客户端 X-Paxos下载失败如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
4月前
|
SQL 关系型数据库 MySQL
MySQL【实践 02】MySQL迁移到PostgreSQL数据库的语法调整说明及脚本分享(通过bat命令修改mapper文件内的SQL语法)
MySQL【实践 02】MySQL迁移到PostgreSQL数据库的语法调整说明及脚本分享(通过bat命令修改mapper文件内的SQL语法)
107 0
|
4月前
|
关系型数据库 数据库 数据安全/隐私保护
postgresql |数据库 |postgresql数据库的短命令详细介绍
postgresql |数据库 |postgresql数据库的短命令详细介绍
19 0
|
4月前
|
Rust 关系型数据库 编译器
Rust调用libpq访问PostgreSQL
Rust调用libpq访问PostgreSQL
75 0
|
9月前
|
前端开发 关系型数据库 测试技术
PostgreSQL 14通过libpq改进logging
PostgreSQL 14通过libpq改进logging
45 0
|
9月前
|
Cloud Native 关系型数据库 分布式数据库
阿里云最新产品手册——阿里云核心产品——云原生关系型数据库PolarDB——一读多写
阿里云最新产品手册——阿里云核心产品——云原生关系型数据库PolarDB——一读多写自制脑图
90 1

热门文章

最新文章