↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1
历史版本PostgreSQL 7.3 已于 2007 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

1.4. 异步查询处理 #

PQexec 函数足以在简单的同步应用中提交命令。但它有几个主要缺陷:

  • PQexec 会等待命令完成。应用可能有其他工作要做(例如维护一个用户界面),这种情况下它不会想要阻塞等待响应。

  • 由于控制权埋在 PQexec 内部,前端很难在需要时决定尝试取消正在进行的命令。(这可以在信号处理器中完成,但别无他法。)

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

不喜欢这些限制的应用可以改用构成 PQexec 的底层函数: PQsendQuery 和 PQgetResult。

使用此功能的旧程序以及 PQputline 和 PQputnbytes 都可能在等待向后端发送数据时阻塞。为了解决这一问题,加入了 PQsetnonblocking 函数。

旧应用可以不使用 PQsetnonblocking 而保持旧的、可能阻塞的行为。新程序则可以使用 PQsetnonblocking 实现到后端的完全非阻塞连接。

  • PQsetnonblocking 设置连接的非阻塞状态。

    int PQsetnonblocking(PGconn *conn, int arg)
    

    arg 为 1 时把连接置为非阻塞状态,arg 为 0 时置为阻塞状态。成功返回 0,出错返回 -1。

    在非阻塞状态下,对 PQputline、PQputnbytes、 PQsendQuery 和 PQendcopy 的调用不会阻塞;如果需要再次调用,它们会返回错误。

    当数据库连接被设置为非阻塞模式后又调用了 PQexec 时,它会临时把连接置为阻塞状态,直到该 PQexec 完成。

    预计不久的将来,libpq 的更多部分将变得对 PQsetnonblocking 功能安全。

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

    int PQisnonblocking(const PGconn *conn)
    

    连接设置为非阻塞模式时返回 1,阻塞时返回 0。

  • PQsendQuery 向服务器提交一个命令而不等待结果。命令成功发出返回 1,否则返回 0(此时用 PQerrorMessage 获取失败的更多信息)。

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

    成功调用 PQsendQuery 后,调用一次或多次 PQgetResult 获取结果。在 PQgetResult 返回 NULL(表示命令已完成)之前,不得(在同一连接上)再次调用 PQsendQuery。

  • PQgetResult 等待先前一次 PQsendQuery 的下一个结果并将其返回。查询完成且不再有结果时返回 NULL。

    PGresult *PQgetResult(PGconn *conn);
    

    必须反复调用 PQgetResult 直到它返回 NULL,表示命令已完成。(如果在没有活跃命令时调用,PQgetResult 会立即返回 NULL。)对 PQgetResult 返回的每个非 NULL 结果,应使用前文所述的同一批 PGresult 访问函数处理。用完每个结果对象后别忘了用 PQclear 释放。注意,只有当查询处于活跃状态且必要的响应数据尚未被 PQconsumeInput 读取时,PQgetResult 才会阻塞。

使用 PQsendQuery 和 PQgetResult 解决了 PQexec 的一个问题:如果命令字符串包含多条 SQL 命令,这些命令的结果可以分别获取。(顺便说一句,这也支持一种简单的重叠处理:前端可以处理某条查询的结果,同时后端继续处理同一命令字符串中后面的查询。)但调用 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,否则忙碌状态永远不会结束。

  • PQflush 尝试刷新排队发往后端的任何数据,成功(或发送队列为空)返回 0,因某种原因失败返回 EOF。

    int PQflush(PGconn *conn);
    

    在非阻塞连接上,调用 select() 判断响应是否到达之前需要先调用 PQflush。返回 0 可确保没有已排队但尚未真正发送到后端的数据。只有使用了 PQsetnonblocking 的应用才需要这样做。

  • PQsocket 获取后端连接套接字的文件描述符号。有效的描述符将 >= 0;结果为 -1 表示当前没有打开的后端连接。

    int PQsocket(const PGconn *conn);
    

    应当用 PQsocket 获取后端套接字描述符,为执行 select() 做准备。这使使用阻塞连接的应用可以同时等待后端响应或其他条件。如果 select() 的结果表明可以从后端套接字读取数据,就应调用 PQconsumeInput 读取数据;随后即可用 PQisBusy、PQgetResult 和/或 PQnotifies 处理响应。

    非阻塞连接(使用了 PQsetnonblocking 的连接)在 PQflush 返回 0、表明没有缓冲数据等待发往后端之前,不应使用 select()。

使用这些函数的典型前端会有一个主循环,用 select 等待它必须响应的所有条件。其中一个条件是后端有可读的输入;用 select 的话说,就是由 PQsocket 标识的文件描述符上有可读数据。主循环检测到输入就绪时,应调用 PQconsumeInput 读取输入。然后可以调用 PQisBusy,若 PQisBusy 返回假(0),接着调用 PQgetResult。还可以调用 PQnotifies 检测 NOTIFY 消息(见 第 1.6 节)。

使用 PQsendQuery/PQgetResult 的前端还可以尝试取消一个仍随后端处理中的命令。

  • PQrequestCancel 请求 PostgreSQL 放弃对当前命令的处理。

    int PQrequestCancel(PGconn *conn);
    

    取消请求成功发出则返回 1,否则返回 0。(若为 0,PQerrorMessage 会说明原因。)但成功发出并不能保证请求会生效。无论 PQrequestCancel 的返回值如何,应用都必须继续用 PQgetResult 按正常顺序读取结果。如果取消生效,当前命令会提前终止并返回一个错误结果。如果取消失败(例如因为后端已完成该命令的处理),则根本不会有可见的结果。

注意,如果当前命令是事务的一部分,取消将中止整个事务。

PQrequestCancel 可以在信号处理器中安全地调用。因此,如果取消的决定可以在信号处理器中做出,也可以将它与普通的 PQexec 配合使用。例如,psql 从 SIGINT 信号处理器中调用 PQrequestCancel,从而允许交互式地取消它通过 PQexec 发出的查询。注意,如果连接当前未打开或后端当前没有在处理命令,PQrequestCancel 将不起作用。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。