2.6.6 循环等待处理查询

完成上述工作后,Postgres可以真正接受客户端查询并处理,系统开始运行。在介绍具体实现之前,先介绍一下在服务进程与用户进程通信的过程中所涉及的数据结构:

共享缓冲区PqSendBuffer:这是一个大小为8192字节的输出缓冲区,用来接收服务器发送给客户端的消息。全局变量PqSendPointer是一个字符指针,表示在PqSendBuffer缓冲区中在此地址之前的消息正等待发送给客户端,以后新加入的消息放在PqSendPointer所指向的位置。

共享缓冲区PqRecvBuffer:它也是一个大小为8192字节的输入缓冲区,用来接受客户端发送给服务器的消息。全局变量PqRecvLength是一个字符指针,表示下一个发送过来的消息在缓冲区中应存放的位置,PqRecvPointer表示在此偏移地址之前的消息已处理,PqRecvPointer和PqRecvLength之间的消息还未被处理。初始化的时候PqRecvLength和PqRecvPointer大小均为0。

Postgres使用一种基于消息的协议用于服务器和客户端之间通信。所有通信都是通过一个消息流进行的。消息的第一个字节标识消息类型,接下来四个字节声明消息剩下部分的长度(这个长度包括长度域自身,但不包括消息类型字节)。剩下的消息内容由消息类型决定。主要有以下几种消息:

启动消息:要开始一个会话,客户端需要打开一个与服务器的连接并且发送一个启动消息。这个消息包括用户名以及用户希望与之连接的数据库;它还标识要使用的协议版本。然后服务器就使用这些信息以及配置文件的内容(比如pg_hba.conf)来判断这个连接是否可以接受,以及需要什么样的额外的认证。

简单查询:客户端发送一条简单查询消息给服务器请求开始一个查询。该消息包含一个用文本字串表达的SQL命令(或者一组命令)。服务器根据查询命令字串的内容发送一条或者更多条响应消息给客户端,并且最后是一条ReadyForQuery响应消息。该消息通知客户端它可以安全地发送新命令了(也表示服务器已经处理完之前发送的命令)。

扩展查询:扩展查询协议把前面的简单查询分割成若干个步骤,准备的步骤可以多次复用以提高效率。另外,还可以获得额外的特性。在扩展的协议里,前端发送一个Parse消息,它包含一个文本查询字串,还有一些有关参数占位符的数据类型的信息,以及一个最终准备好的语句对象的名字。

函数调用:函数调用允许客户端请求对数据库pg_proc系统表中的一个任意函数的直接调用,但是用户必须具有在该函数上的执行权限。

取消正在处理的请求:客户端可以向服务器发送CancelRequest消息来取消正在处理的请求。服务器收到这类消息后将处理这个请求然后关闭连接。出于安全原因,对取消请求消息不做直接的响应。

终止:通常终止过程的方法是客户端发送一条Terminate(终止)消息并且立刻关闭连接。一旦收到消息,服务器将马上关闭连接并且退出。

在第一次进入循环时,服务进程首先会释放上次查询(循环)时的内存,并为新的查询(循环)分配内存,准备好查询执行环境。接着调用ReadyForQuery函数给客户端发送一条消息告诉客户端它已经准备好接收查询了。接下来服务进程就可以调用ReadCommand函数开始从客户端接收消息了。

客户端可以有两种方式递交请求:一种是通过网络连接,这时候我们调用SocketBackend函数接收客户端请求;另一种是客户端和服务器在同一台机器上,这时候我们调用InteractiveBackend函数从交互终端读取客户端请求。

在网络连接的方式下,SocketBackend首先检查PqRecvBuffer中是否存在未处理的消息,如果没有则先要通过网络连接从客户端读取消息到PqRecvBuffer中,然后取出第一条未处理的消息的消息类型。接着SocketBackend根据消息类型设置相应的全局变量,例如当消息类型为Q的时候表明该消息为简单查询,这时候会将全局变量doing_extended_query_message设置为false,然后函数从PqRecvBuffer中读取一条完整的消息到inBuf中。最后函数返回消息的消息类型。

在终端方式下,InteractiveBackend负责从终端读取用户输入的一条完整的消息到inBuf中,此时消息的消息类型设置为Q,表示一个简单消息。

根据前面函数返回的消息类型,服务进程就可以调用相应的函数来具体处理这些消息了。例如,对类型为“Q”的查询(简单查询),Postgres会调用exec_simple_query函数来处理,事实上,几乎所有我们用到的SELECT、UPDATE、DELETE和INSERT语句都是从这里开始执行的。

results matching ""

    No results matching ""