根据启动流程,postgres 会调用 select 监听客户端连接,并 fork 子线程来单独负责这个连接的请求处理,代码:
postmaster.c PostmasterMain() => 主程序入口
postmaster.c ServerLoop() => 开始 main loop,for(;;)调用 linux's epoll_wait(2) 处理客户端链接。
postmaster.c ConnCreate() => 监听到客户端链接..
postmaster.c BackendStartup() => fork子进程程处理当前链接
postmaster.c BackendRun() => 客户端子进程请求处理函数入口。
postgres.c PostgresMain() => 开始客户端处理,for(;;)调用监听客户端请求
postgres.c ReadCommand() => 解析客户端指令,query、parse、bind、execute、close、describe、flush 等等。
客户端请求处理入口函数 PostgresMain(),会 for(;;)监听客户端请求:
void PostgresMain(const char *dbname, const char *username){
int firstchar;//解析出来的客户端请求
// 设置信号处理
if (am_walsender)
WalSndSignals();
else{ //..}
// 预处理
BaseInit();
if (am_walsender)
InitWalSender();
// 开始监听客户端请求
for (;;)
{
// 解析客户端请求
firstchar = ReadCommand(&input_message);
// 然后不同的请求进行不同的处理
switch (firstchar)
{
case 'Q': /* simple query */
case 'P': /* parse */
case 'B': /* bind */
case 'E': /* execute */
case 'F': /* fastpath function call */
case 'C': /* close */
case 'D': /* describe */
case 'H': /* flush */
case 'S': /* sync */
case EOF:
case 'X':
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
default:
}
} /* end of input-reading loop */
}
客户端消息类型如下:
case 'Q'
|
/* simple query */
|
exec_simple_query()
|
执行 insert、query、update 等。非参数化的请求。
|
case 'P'
|
/* parse */
|
exec_parse_message()
|
参数化 Prepared-statement 的解析阶段:parse,对语法进行分析并重写,请求传递的是带参数占位符的sql语句。
|
case 'B'
|
/* bind */
|
exec_bind_message()
|
参数化 Prepared-statement 的解析阶段:bind,将上一阶段的 PreparedStatement 生成执行计划并绑定保存,后续相同的参数化请求可以复用,还需要对已有的计划进行校验。请求传递的是参数、参数格式和返回列格式。
|
case 'E'
|
/* execute */
|
exec_execute_message()
|
参数化 Prepared-statement 的解析阶段:execute,参数和计划 Portal 都有了直接执行请求。
|
case 'F'
|
/* fastpath function call */
|
||
case 'C'
|
/* close */
|
||
case 'D':
|
/* describe */
|
exec_describe_statement_message()
exec_describe_portal_message()
|
客户端可以发送 Describe 消息获取 Statment(parse) 或 Portal(bind) 的元信息,即返回结果的列名,类型等信息,这些信息由 RowDescription 消息携带。如果请求获取 Statement 的元信息,还会返回具体的参数信息。
|
case 'H':
|
/* flush */
|
||
case 'S':
|
/* sync */
|
使用参数化 Prepared-statement 的请求需要 sync 结尾。
|
|
case 'X':
|
|||
case EOF:
|
|||
case 'd':
|
/* copy data */
|
copy子协议
|
|
case 'c':
|
/* copy done */
|
copy子协议
|
|
case 'f':
|
/* copy fail */
|
copy子协议
|