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