searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

postgres中的libpq库

2024-09-25 09:31:57
6
0
Libpq 是为 C 语言开发这提供的关于 PostgreSQL 的接口。Libpq 由相关库函数组成,可以允许客户端程序通过将查询传递给 PostgreSQL 后台服务器并接收这些查询返回的结果。同时,libpq也是其它几个 PostgreSQL应用程接口的基础引擎,包括 C++,Perl,Python,Tcl和ECPGC编程的嵌入式 SQL。libpq协议描述了交互所需的认证握手过程、数据请求应答过程与错误处理过程。
 

建立连接阶段(starup)

用户使用libpq 驱动创建与数据库的连接,并发送授权信息,如果一切正常,服务端会反馈状态信息,连接成功创建。
如上图所示,libpq建立连接阶段大致分为三个步骤:连接建立阶段、加密协商阶段、认证协商阶段,其中连接建立阶段是tpc协议的内容,对应代码实现就是connect 和 accept函数。加密协商和认证协商是libpq通信建立连接阶段的重要流程。
建立连接 用户使用libpq创建连接的流程,从代码阶段主要分为三步: 1.创建 PGconn 类型连接对象 conn; 2.连接数据库(通过接口 PQconnectdb /PQconnectdbParams/ PQsetdbLogin);3.判断连接对象 conn 的状态,若为 CONNECTION_OK ,则连接成功。用户通过 libpq 与 server 建立连接的过程,主要涉及到两种状态类型:轮询状态类型PostgresPollingStatusType和连接状态类型ConnStatusType。
每一个连接都通过 PGconn 对象表示,该对象可以从函数 PQconnectdb,PQconnectdbParams 中或者 PQsetdbLogin 获取信息。需要注意的是,这些对象通常返回一个非空的对象指针。除非由于内存太小导致无法分配 PGconn 对象。PQstatus 函数用来检查在执行查询前连接是否成功的状态。
函数介绍如下:
PQconnectdbParams
该参数用来创建连接到数据库服务器的新的连接。如下:
       PGconn *PQconnectdbParams(const char * const *keywords, const char * const *values, int expand_dbname);
该函数使用传入的两个 NULL 终止的数组中打开一个新的连接,首先,keywords 用来定义一个字符串数组,每一个都是一个关键字。其次,values,为每个关键字给定一个值。当前可识别的关键字有 host、hostaddr、port、dbname、user、password、passfile、connect_timeout、client_encoding、options、application_name、fallback_application_name,keepalives 等信息。常用的通常有 host、hostaddr、port、dbname、user、password 这五个。
PQconnectdb
该函数和 PQconnectdbParams 类似,只不过该函数使用的并非一个 key-value 的值,而是一个字符串数组常量。如下:
         PGconn * PQconnectdb(const char * conninfo);
轮询状态:轮询主要是用于等待 conn 对象创建 socket 、写入连接参数、等待 server 返回结果、等待连接认证等。此过程中 conn 对象已经创建,但未完成与 server 的服务连接过程。
连接不是瞬间完成的,需要有一系列的处理过程,在此过程中的等待流程由轮询控制,是一个短暂的过程。PGRES_POLLING_FAILED 和 PGRES_POLLING_OK 为轮询终止的状态条件, PGRES_POLLING_READING,PGRES_POLLING_WRITING 为需要持续询问当前 conn 的状态条件。
在空的conn 对象建立后,轮询进入初始状态 PGRES_POLLING_WRITING,调用 PQconnectPoll 询问到是否需要等待 conn 建立完成。若仍需等待,则继续等待和状态轮询直到连接建立完成,否则错误返回。
连接状态:PQconnectPoll函数会为客户端连接推进连接状态机,为CONNECTION_MADE状态进行处理,这里的主要工作就是启动认证请求。
在连接过程的任何时候,都可以通过调用PQstatus 来检查连接的状态。如果此时调用返回CONNECTION_BAD,则连接过程失败;如果调用返回 CONNECTION_OK,则连接准备就绪。这两种状态都可以从 PQconnectPoll 的返回值中检测到。
在异步连接过程期间(并且仅在期间)也可能出现其他状态,比如writing ,代表客户端要给服务端发送认证、协商信息;reading 代表等待服务端返回信息包。
PQdb
返回连接的数据库名称
PQuser
返回连接的用户名称
PQpass
返回连接的用户密码
如果连接密码没有被提供,那么将会尝试从 password_file 文件中获取。
PQhost
返回连接的服务器主机名称
PQhostaddr
返回连接的服务器IP地址
PQport
返回连接服务器的端口
PQtty
返回连接的DEBUG 终端信息
PQoptions
返回命令行传递的参数信息
PQstatus
返回连接的状态信息
 
加密协商 加密协商阶段是在连接建立后进行的第一个阶段,为了保证后续的认证协商阶段中会话信息不会泄漏,需要先对连接进行通信加密。
加密协商阶段是可选的,只有开启GSSAPI认证或者SSL认证才会执行。在这一阶段,客户端调用PQconnectPoll函数中,ConnStatusType连接状态处于CONNECTION_NEEDED,然后调用connect函数去连接服务端,连接状态会转变为CONNECTION_STARTED。
这个时候,服务端postmaster会执行如下调用:StreamConnection函数会使用服务器端口创建与客户端的新连接,将 port->sock 设置为新连接的 FD。连接新建成功后,postmaster会调用BackendStartup为该客户端连接创建postgres后端服务子进程。
下一步,PQconnectPoll函数会尝试为此连接推进状态机,为CONNECTION_MADE状态进行处理,启动认证请求,并构建启动包。
如果编译宏参数ENABLE_GSS 或者USE_SSL开启,则进行加密协商。
加密协商过程如下: client−>server(协议版本信息)client−>server(协议版本信息)
加密协商阶段是在连接建立后进行的第一个阶段,为了保证后续的认证协商阶段中会话信息不会泄漏,需要先对连接进行通信加密。 server处理流程: ServerLoop−>BackendStartup−>BackendInitialize−>ProcessStartupPacket(处理加密)ServerLoop−>BackendStartup−>BackendInitialize−>ProcessStartupPacket(处理加密)
认证协商 当加密协商阶段完成或跳过后,libpq 协议将开始进行认证阶段。认证阶段由 Startup message 消息开始,消息格式以消息长度开始,随后紧跟协议版本号,然后是键值对形式的连接信息,如用户名、数据库以及其他 GUC 参数和值。
前端发出Startup message 消息后,后端会进行认证应答,认证应答信息的类型为“R ”,其内容大致分为 3 种情况:完成认证(相当于不需要认证,此时用户不需要验证密码)、提供认证方式与所需的参数、认证错误。
前端通过认证应答信息提供的认证方式(如果有的话)向后端发送认证请求,认证请求消息中包含后端所需要的认证参数,例如密码或密码的MD5 值等。
认证错误消息ErrorResponse 会导致后端直接关闭连接,停止认证协商。
认证请求的类型为“ P ”,其内容需要根据上下文进行推断,例如之前认证应答消息中的认证方式为 MD5,则认证请求消息中的内容就为密码的 MD5 值。
前端向后端发送认证请求后,后端会再次根据认证请求中的内容进行认证应答,直到认证完成或认证错误。所以,认证阶段完成的标志是:后端发送的内容为认证完成的认证应答消息或者发送了ErrorResponse 的认证错误消息。
当认证完成时,后端会在认证应答信息后发送一些其他协议,来通知前端一些必要的参数,其中有:
  • 类型为“ S ”的 ParameterStatus :是一个 Key-value 对,进行参数设置;
  • 类型为“ K ”的 BackendKeyData:描述了一个取消请求的 Key,主要用户在开始阶段时 Cancel request 需要的 Key 值,用于在一个新建会话中中断另一个会话中阻塞操作;
  • 类型为“ Z ”的 ReadyForQuery:代表后端已经准备好开始一个新的数据请求。 至此,一个建立连接的过程已经完全准备完成。建立连接的状态图如下:
数据查询阶段(normal阶段) 数据查询阶段,客户端和服务端所有通信都通过消息流进行。消息的第一个字节标识消息类型,随后四个字节标识消息内容的长度(该长度包括这四个字节本身),具体的消息内容由消息类型决定。
服务端支持消息类型为PostgresMain 函数;客户端支持处理消息类型为pqParseInput3 函数。
数据查询阶段常用的通信模式有三种,分别为Simple query、Extended query和Copy data。
Simple query模式:客户端通过 Query 消息发送一个文本命令给服务端,服务端处理请求,回复查询结果。查询结果通常包括两部分内容:结构和数据。结构通过 RowDescription 消息传递,包括列名、类型 OID 和长度等;数据通过 DataRow 消息传递,每个 DataRow 消息中包含一行数据。
每个命令的结果发送完成之后,服务端会发送一条CommandComplete 消息,表示当前命令执行完成。客户端的一条查询请求可能包含多条 SQL 命令,每个 SQL 命令执行完都会回复一条 CommandComplete 消息,查询请求执行结束后会回复一条 ReadyForQuery 消息,告知客户端可以发送新的请求。消息流如下:
Extended query模式:Extended query 协议将以上 Simple query 的处理流程分为若干步骤,每一步都由单独的服务端消息进行确认。Extended query 协议通常包括5个步骤,分别是 Parse、Bind、Describe、Execute 和 Sync,这里不再展开讲述。
Extended query 协议可以使用服务端的 prepared-statement 功能,即先发送一条参数化 SQL,服务端收到 SQL(Statement)之后对其进行解析、重写并保存,这里保存的 Statement 也就是Prepared-statement,可以被复用;执行 SQL 时,直接获取事先保存的 Prepared-statement 生成计划并执行,避免对同类型 SQL 重复解析和重写;随后,服务端会在适当的条件下缓存计划,以备后续复用。
PGQUERY_EXTENDED查询协议将一个SQL的执行过程拆分成三个层次,相邻的两个层次间抽象出statement和portal对象,每个层次允许单独重复调用,并且在当前连接的生命周期内,也允许再次调用,使整个SQL的执行过程具有了可重复利用性,对中间结果的保存减少了重复调用,节省了执行开销,也提高了执行速度。Extended query 完整消息流如下图所示:
Copy data 模式:为高效地导入/导出数据,libpq 支持 Copy 命令,Copy操作会将当前连接切换至一种截然不同的消息通信方式。
Copy data对应三种模式:
  • copy-in 导入数据,对应命令 COPY FROM STDIN;
  • copy-out 导出数据,对应命令 COPY TO STDOUT;
  • copy-both 用于 walsender,在主备间批量传输数据。
以copy-in 为例,服务端收到 COPY 命令后,进入 COPY 模式,并回复 CopyInResponse。随后客户端通过 Copydata 消息传输数据,CopyComplete 消息标识数据传输完成,服务端收到该消息后,发送 CommandComplete 和 ReadyForQuery 消息,消息流如下图所示:
终止阶段 这一阶段流程相对简单,客户端请求结束后,可以主动发送消息断开连接。服务端接收到客户端的终止消息后,直接退出进程。
总结 通过 libpq 与 PostgreSQL 建立连接是一个比较复杂的过程,主要通过 libpq 所在的 client 端进行驱动:发起请求,等待响应。
在建立连接轮询状态机、建立连接流程状态机和设置环境变量状态机中,有些状态会存在多次转换以完成连接建立的过程。经过连接建立、加密协商、认证协商三个阶段之后,一个连接到PostgreSQL 的 PGconn 连接对象就准备完成,应用程序可以通过该对象进行后续各种业务的执行,向 Server 发起请求,并解析返回结果。
0条评论
0 / 1000
h****n
4文章数
0粉丝数
h****n
4 文章 | 0 粉丝
h****n
4文章数
0粉丝数
h****n
4 文章 | 0 粉丝
原创

postgres中的libpq库

2024-09-25 09:31:57
6
0
Libpq 是为 C 语言开发这提供的关于 PostgreSQL 的接口。Libpq 由相关库函数组成,可以允许客户端程序通过将查询传递给 PostgreSQL 后台服务器并接收这些查询返回的结果。同时,libpq也是其它几个 PostgreSQL应用程接口的基础引擎,包括 C++,Perl,Python,Tcl和ECPGC编程的嵌入式 SQL。libpq协议描述了交互所需的认证握手过程、数据请求应答过程与错误处理过程。
 

建立连接阶段(starup)

用户使用libpq 驱动创建与数据库的连接,并发送授权信息,如果一切正常,服务端会反馈状态信息,连接成功创建。
如上图所示,libpq建立连接阶段大致分为三个步骤:连接建立阶段、加密协商阶段、认证协商阶段,其中连接建立阶段是tpc协议的内容,对应代码实现就是connect 和 accept函数。加密协商和认证协商是libpq通信建立连接阶段的重要流程。
建立连接 用户使用libpq创建连接的流程,从代码阶段主要分为三步: 1.创建 PGconn 类型连接对象 conn; 2.连接数据库(通过接口 PQconnectdb /PQconnectdbParams/ PQsetdbLogin);3.判断连接对象 conn 的状态,若为 CONNECTION_OK ,则连接成功。用户通过 libpq 与 server 建立连接的过程,主要涉及到两种状态类型:轮询状态类型PostgresPollingStatusType和连接状态类型ConnStatusType。
每一个连接都通过 PGconn 对象表示,该对象可以从函数 PQconnectdb,PQconnectdbParams 中或者 PQsetdbLogin 获取信息。需要注意的是,这些对象通常返回一个非空的对象指针。除非由于内存太小导致无法分配 PGconn 对象。PQstatus 函数用来检查在执行查询前连接是否成功的状态。
函数介绍如下:
PQconnectdbParams
该参数用来创建连接到数据库服务器的新的连接。如下:
       PGconn *PQconnectdbParams(const char * const *keywords, const char * const *values, int expand_dbname);
该函数使用传入的两个 NULL 终止的数组中打开一个新的连接,首先,keywords 用来定义一个字符串数组,每一个都是一个关键字。其次,values,为每个关键字给定一个值。当前可识别的关键字有 host、hostaddr、port、dbname、user、password、passfile、connect_timeout、client_encoding、options、application_name、fallback_application_name,keepalives 等信息。常用的通常有 host、hostaddr、port、dbname、user、password 这五个。
PQconnectdb
该函数和 PQconnectdbParams 类似,只不过该函数使用的并非一个 key-value 的值,而是一个字符串数组常量。如下:
         PGconn * PQconnectdb(const char * conninfo);
轮询状态:轮询主要是用于等待 conn 对象创建 socket 、写入连接参数、等待 server 返回结果、等待连接认证等。此过程中 conn 对象已经创建,但未完成与 server 的服务连接过程。
连接不是瞬间完成的,需要有一系列的处理过程,在此过程中的等待流程由轮询控制,是一个短暂的过程。PGRES_POLLING_FAILED 和 PGRES_POLLING_OK 为轮询终止的状态条件, PGRES_POLLING_READING,PGRES_POLLING_WRITING 为需要持续询问当前 conn 的状态条件。
在空的conn 对象建立后,轮询进入初始状态 PGRES_POLLING_WRITING,调用 PQconnectPoll 询问到是否需要等待 conn 建立完成。若仍需等待,则继续等待和状态轮询直到连接建立完成,否则错误返回。
连接状态:PQconnectPoll函数会为客户端连接推进连接状态机,为CONNECTION_MADE状态进行处理,这里的主要工作就是启动认证请求。
在连接过程的任何时候,都可以通过调用PQstatus 来检查连接的状态。如果此时调用返回CONNECTION_BAD,则连接过程失败;如果调用返回 CONNECTION_OK,则连接准备就绪。这两种状态都可以从 PQconnectPoll 的返回值中检测到。
在异步连接过程期间(并且仅在期间)也可能出现其他状态,比如writing ,代表客户端要给服务端发送认证、协商信息;reading 代表等待服务端返回信息包。
PQdb
返回连接的数据库名称
PQuser
返回连接的用户名称
PQpass
返回连接的用户密码
如果连接密码没有被提供,那么将会尝试从 password_file 文件中获取。
PQhost
返回连接的服务器主机名称
PQhostaddr
返回连接的服务器IP地址
PQport
返回连接服务器的端口
PQtty
返回连接的DEBUG 终端信息
PQoptions
返回命令行传递的参数信息
PQstatus
返回连接的状态信息
 
加密协商 加密协商阶段是在连接建立后进行的第一个阶段,为了保证后续的认证协商阶段中会话信息不会泄漏,需要先对连接进行通信加密。
加密协商阶段是可选的,只有开启GSSAPI认证或者SSL认证才会执行。在这一阶段,客户端调用PQconnectPoll函数中,ConnStatusType连接状态处于CONNECTION_NEEDED,然后调用connect函数去连接服务端,连接状态会转变为CONNECTION_STARTED。
这个时候,服务端postmaster会执行如下调用:StreamConnection函数会使用服务器端口创建与客户端的新连接,将 port->sock 设置为新连接的 FD。连接新建成功后,postmaster会调用BackendStartup为该客户端连接创建postgres后端服务子进程。
下一步,PQconnectPoll函数会尝试为此连接推进状态机,为CONNECTION_MADE状态进行处理,启动认证请求,并构建启动包。
如果编译宏参数ENABLE_GSS 或者USE_SSL开启,则进行加密协商。
加密协商过程如下: client−>server(协议版本信息)client−>server(协议版本信息)
加密协商阶段是在连接建立后进行的第一个阶段,为了保证后续的认证协商阶段中会话信息不会泄漏,需要先对连接进行通信加密。 server处理流程: ServerLoop−>BackendStartup−>BackendInitialize−>ProcessStartupPacket(处理加密)ServerLoop−>BackendStartup−>BackendInitialize−>ProcessStartupPacket(处理加密)
认证协商 当加密协商阶段完成或跳过后,libpq 协议将开始进行认证阶段。认证阶段由 Startup message 消息开始,消息格式以消息长度开始,随后紧跟协议版本号,然后是键值对形式的连接信息,如用户名、数据库以及其他 GUC 参数和值。
前端发出Startup message 消息后,后端会进行认证应答,认证应答信息的类型为“R ”,其内容大致分为 3 种情况:完成认证(相当于不需要认证,此时用户不需要验证密码)、提供认证方式与所需的参数、认证错误。
前端通过认证应答信息提供的认证方式(如果有的话)向后端发送认证请求,认证请求消息中包含后端所需要的认证参数,例如密码或密码的MD5 值等。
认证错误消息ErrorResponse 会导致后端直接关闭连接,停止认证协商。
认证请求的类型为“ P ”,其内容需要根据上下文进行推断,例如之前认证应答消息中的认证方式为 MD5,则认证请求消息中的内容就为密码的 MD5 值。
前端向后端发送认证请求后,后端会再次根据认证请求中的内容进行认证应答,直到认证完成或认证错误。所以,认证阶段完成的标志是:后端发送的内容为认证完成的认证应答消息或者发送了ErrorResponse 的认证错误消息。
当认证完成时,后端会在认证应答信息后发送一些其他协议,来通知前端一些必要的参数,其中有:
  • 类型为“ S ”的 ParameterStatus :是一个 Key-value 对,进行参数设置;
  • 类型为“ K ”的 BackendKeyData:描述了一个取消请求的 Key,主要用户在开始阶段时 Cancel request 需要的 Key 值,用于在一个新建会话中中断另一个会话中阻塞操作;
  • 类型为“ Z ”的 ReadyForQuery:代表后端已经准备好开始一个新的数据请求。 至此,一个建立连接的过程已经完全准备完成。建立连接的状态图如下:
数据查询阶段(normal阶段) 数据查询阶段,客户端和服务端所有通信都通过消息流进行。消息的第一个字节标识消息类型,随后四个字节标识消息内容的长度(该长度包括这四个字节本身),具体的消息内容由消息类型决定。
服务端支持消息类型为PostgresMain 函数;客户端支持处理消息类型为pqParseInput3 函数。
数据查询阶段常用的通信模式有三种,分别为Simple query、Extended query和Copy data。
Simple query模式:客户端通过 Query 消息发送一个文本命令给服务端,服务端处理请求,回复查询结果。查询结果通常包括两部分内容:结构和数据。结构通过 RowDescription 消息传递,包括列名、类型 OID 和长度等;数据通过 DataRow 消息传递,每个 DataRow 消息中包含一行数据。
每个命令的结果发送完成之后,服务端会发送一条CommandComplete 消息,表示当前命令执行完成。客户端的一条查询请求可能包含多条 SQL 命令,每个 SQL 命令执行完都会回复一条 CommandComplete 消息,查询请求执行结束后会回复一条 ReadyForQuery 消息,告知客户端可以发送新的请求。消息流如下:
Extended query模式:Extended query 协议将以上 Simple query 的处理流程分为若干步骤,每一步都由单独的服务端消息进行确认。Extended query 协议通常包括5个步骤,分别是 Parse、Bind、Describe、Execute 和 Sync,这里不再展开讲述。
Extended query 协议可以使用服务端的 prepared-statement 功能,即先发送一条参数化 SQL,服务端收到 SQL(Statement)之后对其进行解析、重写并保存,这里保存的 Statement 也就是Prepared-statement,可以被复用;执行 SQL 时,直接获取事先保存的 Prepared-statement 生成计划并执行,避免对同类型 SQL 重复解析和重写;随后,服务端会在适当的条件下缓存计划,以备后续复用。
PGQUERY_EXTENDED查询协议将一个SQL的执行过程拆分成三个层次,相邻的两个层次间抽象出statement和portal对象,每个层次允许单独重复调用,并且在当前连接的生命周期内,也允许再次调用,使整个SQL的执行过程具有了可重复利用性,对中间结果的保存减少了重复调用,节省了执行开销,也提高了执行速度。Extended query 完整消息流如下图所示:
Copy data 模式:为高效地导入/导出数据,libpq 支持 Copy 命令,Copy操作会将当前连接切换至一种截然不同的消息通信方式。
Copy data对应三种模式:
  • copy-in 导入数据,对应命令 COPY FROM STDIN;
  • copy-out 导出数据,对应命令 COPY TO STDOUT;
  • copy-both 用于 walsender,在主备间批量传输数据。
以copy-in 为例,服务端收到 COPY 命令后,进入 COPY 模式,并回复 CopyInResponse。随后客户端通过 Copydata 消息传输数据,CopyComplete 消息标识数据传输完成,服务端收到该消息后,发送 CommandComplete 和 ReadyForQuery 消息,消息流如下图所示:
终止阶段 这一阶段流程相对简单,客户端请求结束后,可以主动发送消息断开连接。服务端接收到客户端的终止消息后,直接退出进程。
总结 通过 libpq 与 PostgreSQL 建立连接是一个比较复杂的过程,主要通过 libpq 所在的 client 端进行驱动:发起请求,等待响应。
在建立连接轮询状态机、建立连接流程状态机和设置环境变量状态机中,有些状态会存在多次转换以完成连接建立的过程。经过连接建立、加密协商、认证协商三个阶段之后,一个连接到PostgreSQL 的 PGconn 连接对象就准备完成,应用程序可以通过该对象进行后续各种业务的执行,向 Server 发起请求,并解析返回结果。
文章来自个人专栏
PG
4 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0