pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
本节描述消息流。有四种不同的流,取决于连接的状态:启动、查询、函数调用和终止。还有针对通知响应和命令取消的特殊规定,它们可以在启动阶段之后的任何时间发生。
启动分为认证阶段和后端启动阶段。
最初,前端发送一个 StartupPacket。postmaster 使用这些信息和 pg_hba.conf(5) 文件的内容来确定前端必须使用什么认证方法。然后 postmaster 以下列消息之一响应:
然后 postmaster 立即关闭连接。
然后 postmaster 移交给后端。postmaster 不再参与通信。
然后前端必须与 postmaster 进行 Kerberos V4 认证对话(此处不描述)。如果成功,postmaster 以 AuthenticationOk 响应,否则以 ErrorResponse 响应。
然后前端必须与 postmaster 进行 Kerberos V5 认证对话(此处不描述)。如果成功,postmaster 以 AuthenticationOk 响应,否则以 ErrorResponse 响应。
然后前端必须发送一个 UnencryptedPasswordPacket。如果密码正确,postmaster 以 AuthenticationOk 响应,否则以 ErrorResponse 响应。
然后前端必须发送一个 EncryptedPasswordPacket。如果密码正确,postmaster 以 AuthenticationOk 响应,否则以 ErrorResponse 响应。
如果前端不支持 postmaster 请求的认证方法,那么它应当立即关闭连接。
发送 AuthenticationOk 之后,postmaster 尝试启动一个后端进程。由于这可能失败,或者后端可能在启动期间遇到失败,前端必须等待后端确认启动成功。前端在此点不应发送任何消息。此阶段后端可能的消息有:
此消息在后端成功启动后发出。它提供前端想要以后能够发出取消请求就必须保存的密钥数据。前端不应响应此消息,而应继续等待 ReadyForQuery 消息。
后端启动成功。前端现在可以发出查询或函数调用消息。
后端启动失败。发送此消息后连接被关闭。
已发出一条警告消息。前端应显示该消息,但仍应继续等待 ReadyForQuery 或 ErrorResponse。
ReadyForQuery 消息与后端在每个查询周期之后将发出的消息相同。取决于前端的编码需要,既可以把 ReadyForQuery 视为一个查询周期的开始(然后 BackendKeyData 表示启动阶段的成功结束),也可以把 ReadyForQuery 视为启动阶段和每个后续查询周期的结束。
查询周期由前端向 backend 发送 Query 消息发起。然后后端根据查询命令串的内容发送一个或多个响应消息,最后发送 ReadyForQuery 响应消息。 ReadyForQuery informs the frontend that it may safely send a new query or function call.
后端可能发送的响应消息有:
一条 SQL 命令正常完成。
后端准备好把数据从前端复制到关系。前端然后应发送 CopyDataRows 消息。后端随后将以带 "COPY" 标签的 CompletedResponse 消息响应。
后端准备好把数据从关系复制到前端。然后它发送 CopyDataRows 消息,再发送带 "COPY" 标签的 CompletedResponse 消息。
查询是 insert(l)、delete(l)、update(l)、fetch(l) 或 select(l) 命令之一。如果事务已被中止,则后端发送带 "*ABORT STATE*" 标签的 CompletedResponse 消息。否则发送以下响应。
对于 insert(l) 命令,后端然后发送带 "INSERT oid rows" where rows is the number of rows inserted, and oid is the object ID of the inserted row if rows is 1, otherwise oid is 0.
对于 delete(l) 命令,后端然后发送带 "DELETE rows" where rows is the number of rows deleted.
对于 update(l) 命令,后端然后发送带 "UPDATE rows" where rows is the number of rows deleted.
对于 fetch(l) 或 select(l) 命令,后端发送一条 RowDescription 消息。然后对返回给前端的每一行, 跟着一条 AsciiRow 或 BinaryRow 消息(取决于是否指定了 二进制游标)。最后,后端发送一条带 "SELECT" 标签的 CompletedResponse 消息。
识别到一个空查询串。(需要特别区分这种情况是历史原因。)
发生了一个错误。
查询串的处理完成。发送单独的消息来指示这一点,因为查询串可能包含多条 SQL 命令。 (CompletedResponse marks the end of processing one SQL command, not the whole string.) ReadyForQuery will always be sent, whether processing terminates successfully or with an error.
发出了与查询有关的警告消息。通知是其他响应之外的附加内容,即后端将继续处理该命令。
每当期待任何其他类型的消息时,前端必须准备好接受 ErrorResponse 和 NoticeResponse 消息。
实际上,即使前端不期待任何类型的消息(即后端名义上空闲)时,NoticeResponse 也可能到达。(特别是,后端可能被其 postmaster 命令终止。在这种情况下它会在关闭连接之前发送一条 NoticeResponse。)建议前端在发出任何新命令之前检查此类异步通知。
此外,如果前端发出任何 listen(l) 命令,那么它必须准备好在任何时间接受 NotificationResponse 消息;见下文。
函数调用周期由前端向后端发送一条 FunctionCall 消息来启动。后端随后根据函数调用的结果发送一条或多条响应消息,最后发送一条 ReadyForQuery 响应消息。ReadyForQuery 告知前端,可以安全地发送新的查询或函数调用。
后端可能发送的响应消息有:
发生了一个错误。
函数调用已执行并返回了结果。
函数调用已执行但没有返回结果。
函数调用处理完成。ReadyForQuery将总是被发送,不管是成功完成处理还是发生一个错误。
发出了与函数调用有关的警告消息。通知是其他响应之外的附加内容,即后端将继续处理该命令。
每当期待任何其他类型的消息时,前端必须准备好接受 ErrorResponse 和 NoticeResponse 消息。 Also, if it issues any listen(l) commands then it must be prepared to accept NotificationResponse messages at any time; see below.
如果前端发出 listen(l) 命令,那么每当为相同的通知名称执行 notify(l) 命令时,后端都会发送一条 NotificationResponse 消息(不要与 NoticeResponse 混淆!)。
通知响应在协议的任何位置(启动之后)都是允许的,除非在另一个后端消息之内。 Thus, the frontend must be prepared to recognize a NotificationResponse message whenever it is expecting any message. Indeed, it should be able to handle NotificationResponse messages even when it is not engaged in a query.
A notify(l) command has been executed for a name for which a previous listen(l) command was executed. Notifications may be sent at any time.
可能值得指出,listen 和 notify 命令中使用的名称不必与 SQL 数据库中关系(表)的名称有任何关系。通知名称只是任意选择的条件名称。
在处理查询期间,前端可以通过向 postmaster 发送一个适当的请求来请求取消该查询。出于实现效率的原因,取消请求不直接发送给后端:我们不希望后端在查询处理期间不断检查来自前端的新输入。取消请求应当相对少见,所以我们把它们做得稍微繁琐一些,以避免正常情况下的性能损失。
要发出取消请求,前端打开一个到 postmaster 的新连接并发送 CancelRequest 消息,而不是通常通过新连接发送的 StartupPacket 消息。postmaster 将处理此请求然后关闭连接。出于安全原因,不对取消请求消息作直接回复。
CancelRequest 消息将被忽略,除非它包含与连接启动期间传递给前端相同的密钥数据(PID 和密钥)。如果该请求与某个当前正在执行的后端的 PID 和密钥匹配,postmaster 向该后端发出信号以中止当前查询的处理。
取消信号可能有效也可能无效——例如,如果它到达时后端已经处理完查询,那么它就不会有效果。如果取消有效,则会导致当前命令提前终止并附带一条错误消息。
这么做是对安全性和效率通盘考虑的结果,前端没有直接的方法获知一个取消请求是否成功。它必须继续等待后端对查询的响应。发出一个取消仅仅是增加了当前查询快些结束的可能性,同时也增加了当前查询会伴随着一条错误消息失败而不是成功执行的可能性。
由于取消请求是发送给 postmaster 而不是通过常规的前端/后端通信链路,任何进程都可能发出取消请求,而不只是其查询要被取消的那个前端。这在构建多进程应用方面可能带来一些灵活性上的好处。它也引入了安全风险,即未授权的人可能试图取消查询。这一安全风险通过要求在取消请求中提供动态生成的密钥来解决。
正常的优雅终止过程是前端发送 Terminate 消息并立即关闭连接。收到该消息后,后端立即关闭连接并终止。
不优雅的终止可能由于任一端的软件故障(即核心转储)而发生。如果前端或后端看到连接意外关闭,应当清理并终止。 The frontend has the option of launching a new backend by recontacting the postmaster, if it doesn't want to terminate itself.
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。