协议对启动和正常操作有不同的阶段。在启动阶段,前端向服务器打开一个连接,并且向服务器证明自己是可信的。(这可能包含一个消息,或多个消息,具体取决于正在使用的认证方法。)如果一切顺利,服务器接着向前端发送状态信息,最后进入正常操作模式。除了初始化启动请求消息外,协议的这一部分由服务器驱动。
在正常运行过程中,前端会将查询和其他命令发送到后端,然后后端将查询结果和其他响应发送回前端。后端将在少数情况下(例如 NOTIFY
中)发送主动消息,但这一部分会话基本上由前端请求驱动。
会话终止通常由前端选择,但在某些情况下,可以由后端强制终止。无论哪种情况下,当后端关闭连接时,它都会在退出前回滚所有打开(不完整)的事务。
在正常运行过程中,可以通过两个子协议中的任何一个来执行 SQL 命令。在 “简单查询” 协议中,前端只需要发送一个文本文本查询字符串,后端会对它进行分析并立即执行。在 “扩展查询” 协议中,查询处理被分成多个步骤:分析、绑定参数值和执行。这提供了灵活性,还有性能优势,但复杂性也有所增加。
正常运行还有其他子协议来处理特殊操作,例如 COPY
。
所有通信都会通过一个消息流进行。消息的第一个字节会识别消息类型,接下来的四个字节会指定消息其余部分的长度(这个长度计数包括它自己,但不包括消息类型字节)。消息的其余内容由消息类型确定。由于历史原因,客户端发送的第一个消息(启动消息)没有初始消息类型字节。
为了避免丢失与消息流的同步,服务器和客户端通常会将整个消息读入一个缓冲区(使用字节计数),然后才会尝试处理其内容。如果在处理内容时检测到错误,这可以实现轻松恢复。在极端情况下(例如,没有足够的内存来缓冲消息),接收方可以使用字节计数来确定在恢复读取消息之前要跳过多少输入内容。
相反,服务器和客户端都必须注意绝不发送不完整的消息。这通常可以通过在开始发送之前将整个消息编制到一个缓冲区中来实现。如果通信故障发生在发送或接收消息到一半时,唯一合理的响应是中断连接,因为恢复消息边界同步几乎没有希望。
在扩展查询协议中,SQL 语句执行分为多个步骤。步骤之间保留的状态由两类对象表示:预处理语句 和 门户。预处理语句表示对文本查询字符串进行解析和语义分析的结果。预处理语句本身不能执行,因为它可能缺少 参数 的特定值。门户表示一个准备好执行或已部分执行的语句,其中所有缺失的参数值已填入。(对于 SELECT
语句,门户等同于打开的光标,但我们选择使用不同的术语,因为光标不处理非 SELECT
语句。)
整体执行周期包括 解析 步骤,它从文本查询字符串创建预处理语句;绑定 步骤,它给定预处理语句和任何所需参数的值创建门户;以及运行门户查询的 执行 步骤。对于返回行的查询(SELECT
、SHOW
等),执行步骤可以被告知只获取有限数量的行,因此可能需要多个执行步骤来完成操作。
后端可以跟踪多个预处理语句和门户(但请注意,这些只存在于会话中,并且绝不会在会话之间共享)。通过创建时分配的名称引用现有的预处理语句和门户。此外,还存在 “未命名” 预处理语句和门户。虽然这些在很大程度上表现得与命名对象相同,但对它们的处理优化了只执行一次查询然后丢弃它的情况,而对命名对象的处理则针对多次使用的期望进行了优化。
特定数据类型的数据可以用任何几种不同 格式 传输。截至 PostgreSQL 7.4,唯一支持的格式是 “文本” 和 “二进制”,但该协议为将来的扩展做好了规定。任何值的所需格式都由 格式代码 指定。客户端可以为每个传输的参数值和查询结果的每一列指定格式代码。文本的格式代码为零,二进制的格式代码为一,所有其他格式代码都保留给未来定义。
值的文本表示是特定数据类型输入/输出转换函数产生和接受的任何字符串。在传输表示中,没有尾随空字符;前端必须将一个空字符添加到接收到的值,如果它想将它们作为 C 字符串进行处理。(顺便说一句,文本格式不允许嵌入空值。)
整数的二进制表示使用网络字节顺序(最高有效字节在前)。对于其他数据类型,请参阅文档或源代码以了解二进制表示。请记住,复杂数据类型的二进制表示可能会因服务器版本而异;文本格式通常是更具可移植性的选择。