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

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.4 已于 2010 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

27.4. 异步命令处理 #

PQexec函数足以满足普通同步应用程序提交命令的需要。不过,它有两个缺点,对某些用户可能很重要:

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

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

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

不喜欢这些限制的应用可以改用构成 PQexec 的底层函数: PQsendQuery 和 PQgetResult。还有 PQsendQueryParams 和 PQsendQueryPrepared,它们与 PQgetResult 配合使用,可分别实现 PQexecParams 和 PQexecPrepared 的功能。

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 协议的连接,且查询字符串中只允许一个命令。

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 协议的连接。

PQgetResult

等待先前一次 PQsendQuery、 PQsendQueryParams 或 PQsendQueryPrepared 调用的下一个结果并将其返回。命令完成且不再有结果时返回空指针。

PGresult *PQgetResult(PGconn *conn);

必须反复调用 PQgetResult 直到它返回空指针,表示命令已完成。(如果在没有活跃命令时调用,PQgetResult 会立即返回空指针。)对 PQgetResult 返回的每个非空结果,应使用前文所述的同一批 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,否则繁忙状态将永远不会结束。

使用这些函数的典型应用程序会在主循环中通过 select() 或 poll() 等待需要响应的各种条件。其中一个条件是服务器有可读取的输入;对于 select(),这意味着 PQsocket 标识的文件描述符上有可读数据。主循环检测到输入就绪时,应调用 PQconsumeInput 读取输入,然后调用 PQisBusy。如果 PQisBusy 返回假(0),就可以接着调用 PQgetResult。还可以调用 PQnotifies 检测 NOTIFY 消息(见 第 27.6 节)。

使用 PQsendQuery/PQgetResult 的客户端还可以尝试取消一个仍在服务器中处理的命令。

PQrequestCancel

请求服务器放弃对当前命令的处理。

int PQrequestCancel(PGconn *conn);

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

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

PQrequestCancel 可以在信号处理器中安全地调用。因此,如果取消的决定可以在信号处理器中做出,也可以将它与普通的 PQexec 配合使用。例如,psql 从 SIGINT 信号处理器中调用 PQrequestCancel,从而允许交互式地取消它通过 PQexec 发出的命令。

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

PQsetnonblocking

设置连接的非阻塞状态。

int PQsetnonblocking(PGconn *conn, int arg);

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

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

请注意,PQexec不遵守非阻塞模式;如果调用它,它将以阻塞方式执行。

PQisnonblocking

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

int PQisnonblocking(const PGconn *conn);

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

PQflush

尝试将发送队列中的输出数据发送到服务器。成功(或发送队列为空)时返回 0;因某种原因失败时返回 -1;如果尚未能发送队列中的全部数据,则返回 1(这种情况只可能发生在非阻塞连接上)。

int PQflush(PGconn *conn);

在非阻塞连接上发送任何命令或数据之后,调用 PQflush。如果返回 1,等待套接字变为可写后再次调用它;重复直到返回 0。一旦 PQflush 返回 0,等待套接字变为可读,然后按前述方法读取响应。

提交更正

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