有没有做海报的网站推荐,唐山手机网站建设,页面设计按钮,网站建设一般多少钱app引言
PostgreSQL 使用基于消息的协议在前端#xff08;客户端#xff09;和后端#xff08;服务器#xff09;之间进行通信。该协议通过 TCP/IP 和 Unix 域套接字支持。
《深度解析 PostgreSQL Protocol v3.0》系列技术贴#xff0c;将带大家深度了解 PostgreSQL Protoc…
引言
PostgreSQL 使用基于消息的协议在前端客户端和后端服务器之间进行通信。该协议通过 TCP/IP 和 Unix 域套接字支持。
《深度解析 PostgreSQL Protocol v3.0》系列技术贴将带大家深度了解 PostgreSQL Protocol v3.0在 PostgreSQL 7.4 及更高版本中实现有关早期协议版本的描述请参考 PostgreSQL 文档的早期版本该系列文章不予赘述相关的消息传输格式和格式码、消息支持的数据类型、消息的格式、协议交互流程、错误消息和通知消息、支持的子协议等相关的代码解读基于 PostgreSQL 代码仓库的 REL_14_STABLE 分支。
本期是《深度解析 PostgreSQL Protocol v3.0》系列技术贴的第二期文章在第一期文章中带大家解读了 PostgreSQL Protocol v3.0一—概述本期将为大家分享 PostgreSQL Protocol v3.0二 — 扩展查询功能的内容。
一、扩展查询介绍
扩展查询Extended Query协议将简单查询协议分解为多个步骤为提高效率可多次重复使用准备Prepare步骤的结果。此外扩展查询协议还提供了其他功能例如可以将数据值作为单独的参数提供而不必将它们直接插入到查询字符串中。
在扩展查询协议中客户端首先发送一条 Parse 消息其中包含文本查询字符串、可选的参数占位符的数据类型信息以及目标准备语句对象的名称目标准备语句对象名称为空字符串则选择未命名的准备语句。响应为 ParseComplete 或 ErrorResponse 消息。参数数据类型可以由 OID 指定如果没有指定参数数据类型解析器将尝试以与无类型的文本字符串常量相同的方式推断数据类型。
Parse 过程需要注意
1参数的数据类型可以不指定此时设置参数数据类型的 OID 为 0或者使参数数据类型 OID 的数组比查询字符串中使用的参数符号的数量$n短。另一种特殊情况是参数的数据类型可以指定为 void即 void 伪类型的 OID。
这意味着允许实际上是输出 OUT 使用的参数符号作为函数的入参使用。通常情况下没有可以使用 void 参数的上下文但如果函数的参数列表中出现了这样的参数符号则实际上会忽略它。例如如果将 $3 和 $4 指定为具有 void 类型则诸如 foo($1, $2, $3, $4) 之类的函数调用可以匹配具有两个 IN 和两个 OUT 参数的函数。
2Parse 消息中包含的查询字符串不能包含多个 SQL 语句否则报告语法错误。这种限制在简单查询协议中不存在但在扩展查询协议中确实存在因为允许准备语句或门户包含多个 SQL 命令会使协议过度复杂化。
如果成功创建命名的准备语句对象除非明确销毁它否则它将持续到当前会话结束。未命名的准备语句只持续到处理下一个指定未命名语句为目标的 Parse 语句为止。
需要特别注意的是简单查询消息 Query 也会销毁未命名的准备语句。命名的准备语句必须显式关闭然后才能被另一个 Parse 消息重新定义但这对于未命名语句来说不是必需的。还可以使用 PREPARE 和 EXECUTE 在 SQL 命令级别创建和访问命名的准备语句。
一旦准备语句存在就可以使用 Bind 消息为执行做好准备。Bind 消息提供源准备语句的名称空字符串表示未命名的准备语句、目标门户的名称空字符串表示未命名的门户以及准备语句中每个参数占位符的值。提供的参数集必须与准备语句所需的参数集匹配。
如果在 Parse 消息中声明了任何 void 参数在 Bind 消息中为它们传递 NULL 值。Bind 还指定用于查询返回数据的格式返回数据格式可以整体指定也可以按列指定。Bind 消息的响应为 BindComplete 或 ErrorResponse 消息。
Bind 过程需要注意文本和二进制输出之间的选择取决于 Bind 中给出的格式代码而不考虑所涉及的 SQL 命令。当使用扩展查询协议时游标CURSOR声明中的 BINARY 属性无关紧要。
查询的计划生成通常在处理 Bind 消息时进行。如果准备语句没有参数或者被重复执行服务器可能会缓存创建的计划并在同一准备语句的后续 Bind 消息中重用它。但是只有当服务器发现可以创建的通用计划的效率比依赖于提供的特定参数值的计划高得多时它才会这样做。但是就扩展协议而言这个过程是透明的。
如果成功创建命名门户对象除非明确销毁它否则它将持续到当前事务结束。在事务结束时或在发出指定未命名门户为目标的下一个 Bind 语句时将销毁未命名门户。需要特别注意的简单查询的消息 Query 也会销毁未命名的门户。在可以通过另一个 Bind 消息重新定义命名门户之前必须显式关闭命名门户但这对于未命名门户不是必需的。命名门户也可以使用 DECLARE CURSOR 和 FETCH 在 SQL 命令级别创建和访问。
一旦门户存在就可以使用 Execute 消息执行它。Execute 消息指定门户名称空字符串表示未命名的门户和最大结果行计数0 表示“获取所有行”。结果行计数仅对包含返回行数据集的命令的门户有意义在其他情况下命令始终执行直至完成并且忽略行计数。对 Execute 消息的可能响应与通过简单查询协议发出的 Query 消息的响应相同但 Execute 不会导致 Server 端发出 ReadyForQuery 和 RowDescription 消息。
如果 Execute 在门户执行完成之前终止由于达到非 0 结果行计数服务器端将发送 PortalSuspended 消息。PortalSuspended 消息的出现告诉客户端应针对同一门户发出另一个 Execute 消息以完成操作。在门户执行完成之前不会发送指示源 SQL 命令执行完成的 CommandComplete 消息。因此Execute 阶段总是由以下消息之一的出现而终止CommandComplete、EmptyQueryResponse如果门户是从空查询字符串创建的、ErrorResponse 或 PortalSuspended。
在完成每个扩展查询消息系列时客户端应发出一条 Sync 消息。如果当前事务不在 BEGIN/COMMIT 显式事务块内则无参数 Sync 消息会导致服务器端关闭当前事务这里的“关闭”表示如果没有错误则提交如果错误则回滚。然后发出 ReadyForQuery 响应。Sync 消息的目的是为错误恢复提供新的同步点。
当在处理任何扩展查询消息时检测到错误服务器端会发出 ErrorResponse 消息然后读取并丢弃消息直至收到 Sync 消息然后发出 ReadyForQuery 消息返回正常消息处理状态。但是需要注意如果在处理 Sync 消息时检测到错误则不会跳过该消息处理这确保每个 Sync 消息都有且只有一个 ReadyForQuery 响应消息发送到客户端。
另外Sync 消息不会导致用 BEGIN 打开的事务块关闭。ReadyForQuery 消息包含事务状态信息因此可以检测到这种情况。
除了上述必备的基本操作之外还有几个可选操作可用于扩展查询协议
Describe 消息门户描述变体指定现有门户的名称或空字符串指示未命名门户。响应是一条 RowDescription 消息描述门户执行将返回的数据行如果门户不包含将返回数据行的查询则返回 NoData 消息如果指定的门户不存在则返回 ErrorResponse 消息。
Describe 消息语句描述变体指定现有准备语句的名称或空字符串指示未命名准备语句。响应是一条 ParameterDescription 消息描述指定语句所需的参数紧跟着是一条 RowDescription 消息描述最终执行语句时将返回的数据行如果该语句不返回数据行则返回 NoData 消息。
如果指定的准备语句不存在则会响应 ErrorResponse 消息。需要注意的是由于尚未收到 Bind 消息因此服务器端还不知道用于数据返回的列格式在这种情况下RowDescription 消息中的格式代码字段将为 0。
在大多数情况下客户端应该在发送 Execute 消息之前发送一个门户或语句 Describe 变体消息以确保它知道如何解释将返回的结果。
Close 消息关闭现有的准备语句或门户并释放资源。对不存在的语句或门户名称发出 Close 消息不会引起错误。Close 消息的响应通常是 CloseComplete但如果在释放资源时遇到一些困难则可能是 ErrorResponse 消息。需要注意的是关闭准备语句会隐式关闭由该语句构建的所有打开的门户。
Flush 消息不会导致生成任何特定的输出但会强制服务器端发送其输出缓冲区中已经存在的任何数据。如果客户端希望在发出更多命令之前检查该命令的结果则必须在除 Sync 之外的任何扩展查询命令之后发送 Flush。如果没有 Flush 消息服务器端返回的消息将被组合成尽可能少的数据包数量以最大限度地减少网络开销。
因此简单查询消息 Query 大致相当于 Parse、Bind、portal-Description、Execute、Close、Sync 系列命令使用未命名的准备语句和门户的对象不使用参数。
不同之处在于
1简单查询的 Query 消息将接受查询字符串中的多个 SQL 语句自动为每个语句连续执行 bind/describe/execute 命令序列
2简单查询的 Query 消息的响应不会是 ParseComplete、BindComplete、CloseComplete 和 NoData 消息。
二、扩展查询支持流水线操作
扩展查询协议允许流水线Pipelining操作这意味着可以发送一系列查询语句而无需等待较早的查询完成。这减少了完成一系列给定操作所需的网络往返次数。但是如果其中一个步骤失败用户必须仔细考虑处理错误所需的行为因为后续的查询已经在向服务器发送。
处理这一问题的一种方法是使整个查询系列成为一个单独的事务即将查询序列封装在 BEGIN…COMMIT 中。然而如果希望某些命令独立于其他命令进行单独提交该方法并没有帮助。
扩展查询协议提供了另一种管理此问题的方法即省略在依赖步骤之间发送同步消息。由于在发生错误后服务器端将跳过命令消息直至找到 Sync 消息这允许在前一个命令失败时自动跳过流水线中的后一个命令而客户端不必使用 BEGIN 和 COMMIT 显式管理命令序列。流水线中可独立提交的段可以通过 Sync 消息来分隔。
如果客户端没有发出显式的 BEGIN那么如果前面的步骤成功则每个 Sync 通常会导致事务隐式的 COMMIT如果失败则会引起事务隐式的 ROLLBACK。但是有一些 DDL 命令如 CREATE DATABASE无法在事务块内执行。如果一个这种 DDL 命令在流水线中执行除非它是流水线中的第一个命令否则都将失败。
此外这种 DDL 命令一旦执行成功它将强制立即提交以保持数据库一致性。因此在其中一个命令之后立即发送 Sync 消息除了使用 ReadyForQuery 进行响应之外没有任何效果。使用此方法时必须通过 ReadyForQuery 消息计数并等待达到发送的 Sync 消息数来确定流水线的完成情况。计算命令完成响应数量是不可靠的因为一些命令可能会被跳过因此不会产生完成消息。
三、扩展查询可能出现的消息 四、扩展查询涉及消息的格式
以下是在扩展查询过程中可能出现的消息下面进行详细介绍。
1.Parse(F:‘P’) Parse 消息格式如下 Byte1(‘P’) 将消息标识为 Parse 命令。
Int32 消息内容的字节长度包括自身 4 个字节。
String 目标准备语句的名称空字符串选择未命名的准备语句。
String 要解析的查询字符串。
int16 指定的参数数据类型的数目可以为 0。请注意这并不是可能出现在查询字符串中的参数数量的指示只是前端希望为其预先指定数据类型的数量。
然后对于每一个参数都有以下数据类型 OID
Int32 指定参数数据类型的 OID。在此处放置 0 相当于未指定类型。 2. ParseComplete(B:‘1’) ParseComplete 消息格式如下
Byte1(‘1’) 将消息标识为 ParseComplete 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。始终为 4。
3. Bind(F:‘B’) Bind 消息格式如下 Byte1(‘B’) 将消息标识为 Bind 消息。
Int32 消息内容的字节长度包括自身 4 个字节。
String 目标门户的名称空字符串选择未命名的门户。
String 源准备语句的名称空字符串选择未命名的准备语句。
Int16 后续的参数格式代码的数量下面用C表示。值可以是 10表示没有参数或参数都使用默认格式text 21在这种情况下指定的格式代码被应用于所有参数 3值等于参数的实际数量。
Int16[C] 参数格式代码。目前数组每个元素的值必须为 0text或 1binary。
Int16 后面的参数值的数目可能为 0。值必须与查询所需的参数数量相匹配。
接下来将为每个参数构建以下字段对
Int32 参数值的长度以字节为单位此计数不包括其自身。值可以为零。作为一种特殊情况-1 表示 NULL 参数值。值为 NULL 情况下后面没有值字节。
Byten 参数的值格式由关联的格式代码指示。n 是上述参数值的长度。 在最后一个参数之后将显示以下两个字段
Int16 后面的结果列格式代码的数量下面用 R 表示。值可以是 10表示没有结果列或者结果列都应该使用默认格式text 21在这种情况下指定的格式代码被应用于所有结果列如果有结果列的话 3等于查询的结果列的实际数量。
Int16[R] 结果列格式代码。目前数组每个元素的值必须为0text或1binary。 4. BindComplete(B:‘2’) BindComplete 消息格式如下
Byte1(‘2’) 将消息标识为 BindComplete 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。始终为 4。
5. Describe(F:‘D’) Describe 消息格式如下
Byte1(‘D’) 将消息标识为 Describe 消息。
Int32 消息内容的字节长度包括自身 4 个字节。
Byte1 指示 Describe 描述的对象类型。可能的值为‘S’表示描述准备语句‘P’表示描述门户。
String 要描述的准备语句或门户的名称空字符串选择未命名的准备语句或门户。
描述门户 Portal 的消息格式如下 描述准备语句的消息格式如下 6. RowDescription(B:‘T’) RowDescription 消息格式如下 Byte1(‘T’) 将消息标识为行描述。
Int32 消息内容的字节长度包括自身 4 个字节。
Int16 指定一行中的字段数可以为 0。
对于行描述中的每个字段都有以下 7 部分内容
String 字段名称
Int32 如果字段可以被标识为特定表的列则值为该表的对象 ID否则为 0。
Int16 如果该字段可以标识为特定表的列则值为该列的属性编号否则为 0。
Int32 字段数据类型的对象 ID。
Int16 数据类型大小可以参考 pg_type.typlen。需要注意的是负值表示可变宽度类型。
Int32 类型修饰符可以参考 pg_attribute.atttypmod。修饰符的含义是特定于数据类型的。
Int16 字段的格式代码。目前只能是 0文本或 1二进制。在从 Describe 语句请求返回的 RowDescription 中格式代码还未知并且始终为零。
蓝色背景的部分是每个列的详细描述。浅灰色背景的部分是可选的格式是蓝色部分的重复。
7. NoData(B:‘n’) NoData 消息格式如下 Byte1(‘n’) 将消息标识为 NoData 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。值始终为 4。
8. ParameterDescription(B:‘t’) ParameterDescription 消息格式如下 Byte1(‘t’) 将消息标识为 ParameterDescription 消息。
Int32 消息内容的字节长度包括自身 4 个字节。
Int16 语句使用的参数数量可以为 0。
然后对于每个参数都有以下内容
Int32 指定参数数据类型的对象 ID。
如果语句使用的参数数量为 0则没有后续的参数数据类型的内容。蓝色背景的部分是每个参数的数据类型对象 ID是可选的。浅灰色背景的部分是可选的格式是蓝色部分的重复。
9. Execute(F:‘E’) Execute 消息格式如下
Byte1(‘E’) 将消息标识为 Execute 消息。
Int32 消息内容的字节长度包括自身 4 个字节。
String 待执行门户的名称空字符串选择未命名的门户。
Int32 如果门户包含返回行的查询值为返回的最大行数否则忽略该值。0 表示不限制返回行数。
10. DataRow(B:‘D’) DataRow 消息格式如下 Byte1(‘D’) 将消息标识为数据行。
Int32 消息内容的字节长度包括自身 4 个字节。
Int16 后续列值的数量可能为 0。
接下来为每列的值显示以下一对字段
Int32 列值的长度以字节为单位此计数不包括长度字段自身的 4 个字节。该字段值可以为零。作为一种特殊情况-1 表示列值为 NULL。在列值为 NULL 的情况下后面没有值字节的字段。
Byten 列的值格式由关联的格式代码指示。n 是上述字段的长度值。
蓝色背景的部分是每个列值的字节长度和列值是可选的。浅灰色背景的部分是可选的格式是蓝色部分的重复。
11. CommandComplete(B:‘C’) CommandComplete 消息格式如下
Byte1(‘C’) 将消息标识为命令完成。
Int32 消息内容的字节长度包括自身 4 个字节。
String 命令标记。这通常是一个单词用于标识完成了哪个 SQL 命令。 1对于 INSERT 命令标记是 INSERT oid rows其中 rows 是插入的行数。如果 rows 为 1 并且目标表具有 oid则 oid 表示插入行的对象 ID。但 OIDs 系统列不再受支持因此 oid 总是 0 2对于 DELETE 命令标记为 DELETE rows其中 rows 是删除的行数 3对于 UPDATE 命令标记是 UPDATE rows其中 rows 是更新的行数 4对于 SELECT 或 CREATE TABLE AS 命令标记是 SELECT rows其中 rows 是检索的行数 5对于 MOVE 命令标记为 MOVE rows其中 rows 是游标位置已更改的行数 6对于 FETCH 命令标记是 FETCH rows其中 rows 是从游标检索的行数 7对于 COPY 命令标记是 COPY rows其中 rows 是复制的行数。注意行计数仅出现在 PostgreSQL 8.2 及更高版本中。
12. PortalSuspended(B:‘s’) PortalSuspended 消息格式为 Byte1(‘s’) 将消息标识为门户已挂起消息。请注意只有当达到 Execute 消息的行计数限制时才会出现此信息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。值始终为 4。
13. EmptyQueryResponse(B:‘I’) EmptyQueryResponse 消息格式如下
Byte1(‘I’) 将消息标识为对空查询字符串的响应。EmptyQueryResponse 消息将替代 CommandComplete 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。值始终为 4。
Close(F:‘C’)
Close 消息格式如下 Byte1(‘C’) 将消息标识为 Close 消息。
Int32 消息内容的字节长度包括自身 4 个字节。
Byte1 指示 Close 的对象类型。可能的值为‘S’表示关闭准备语句‘P’表示关闭门户。
String 要关闭的准备语句或门户的名称空字符串选择未命名的准备语句或门户。
关闭门户 Portal 的消息格式如下 关闭准备语句的消息格式如下 15. CloseComplete(B:‘3’) CloseComplete 消息格式如下 Byte1(‘3’) 将消息标识为 CloseComplete 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。始终为 4。
16.Flush(F:‘H’) Flush 消息格式如下 Byte1(‘H’) 将消息标识为 Flush 消息。
Int32(4) 消息内容的字节长度包括自身 4 个字节。始终为 4。
17.Sync(F:‘S’) Sync 消息格式如下 Byte1(‘S’) 将消息标识为 Sync 消息。
Int32(4)
消息内容的字节长度包括自身 4 个字节。值始终为 4。
18.ErrorResponse(B:‘E’) ErrorResponse 消息格式如下 Byte1(‘E’) 将消息类型标识为错误。
Int32 消息内容的字节长度包括自身 4 个字节。
消息体 消息体由一个或多个标识字段组成后跟一个’\0’字节作为终止符。字段可以按任何顺序出现。对于每个字段都有以下内容
1Byte1 标识字段类型的代码。如果为零则这是消息终止符后面没有字符串。由于将来可能会添加更多的字段类型客户端应默默忽略无法识别类型的字段
2String 字段值。
蓝色背景的部分是消息体的一个字段类型及其值以及结束符’\0’。一个 ErrorResponse 消息至少包含一个消息体浅灰色背景的部分是可选的格式是蓝色部分的重复。消息体的最后有一个’\0’作为消息的结束。
19.ReadyForQuery(B:‘Z’) ReadyForQuery 消息格式如下 Byte1(‘Z’) 将消息标识为服务器端准备好接收客户端的查询请求类型。每当服务器为新的查询周期做好准备时就会发送 ReadyForQuery。
Int32(5) 消息内容的字节长度包括自身 4 个字节。值总是 5。
Byte1 当前服务器端事务状态指示器。可能的值为 1‘I’: 处于空闲状态不在事务块中 2‘T’: 在事务块中 3‘E’: 在失败的事务块中查询将被拒绝直到事务块结束。
五、扩展查询交互流程
扩展查询协议将简单查询协议分解为多个步骤可以多次重复使用准备步骤的结果以此提高效率。此外扩展查询还提供了其他功能例如可以将数据值作为单独的参数提供而不必将其直接插入查询字符串中。
扩展查询交互流程主要有两种场景 1扩展查询的必要交互流程 2扩展查询带描述准备语句的交互流程。
1. 扩展查询的必要交互流程
扩展查询必要的交互流程主要是 Parse、Bind、portal-Describe、Execute、Sync 等。扩展查询的主要流程如下 1先发送包含单条 SQL 语句的 Parse 消息创建准备语句和 Portal方便后续步骤重复利用 2通过 Bind 消息提供准备语句中参数占位符需要的参数值 3通过 Describe 消息获取 Portal 执行将要返回的数据行的信息。该步骤是可选的 4发送 Execute 消息执行当前的 Portal并返回执行结果的数据 5执行完上述流程发送 Sync 消息提交数据 6可以继续重复执行步骤 2~5的 Bind、Describe、Execute、Sync 流程进行数据的读写。
具体的交互消息和交互流程如下图所示 2. 扩展查询带描述准备语句的交互流程
扩展查询带描述准备语句的交互流程主要是 Parse、准备语句 Describe、Bind、Execute、Sync 等主要是在 Parse 和 Bind 之间发送准备语句的描述请求消息。主要流程如下 1 先发送包含单条 SQL 语句的 Parse 消息创建准备语句和 Portal方便后续步骤重复利用 2发送准备语句 Describe服务器端响应 ParameterDescription 消息和 RowDescription 消息。ParameterDescription 消息描述准备语句需要的参数信息RowDescription 消息描述返回数据行的列布局信息 3通过 Bind 消息提供准备语句中参数占位符需要的参数值 4通过 Describe 消息获取 Portal 执行将要返回的数据行的信息。该步骤是可选的 5发送 Execute 消息执行当前的 Portal并返回执行结果的数据 6执行完上述流程发送 Sync 消息提交数据 7可以继续重复执行步骤3~6的 Bind、portal-Describe、Execute、Sync 流程进行数据的读写。
具体的交互消息和交互流程如下图所示 END
关于技术流系列博客 开物成务厚积薄发。技术流系列博客-以“短小精悍”的形式普及数据库硬核技术。相信大家的每一次阅读都会距离数据库内核更近一步。每一份来自你们的关注都是我们坚持输出的满满能量