在数据库的使用中,增删改查这种操作每天都在进行,本文通过gdb工具演示了一个insert语句的执行流程。
一、gdb增加断点
开启一个session,获取pid
另开一个窗口,用gdb进入调试状态
数据库端执行插入操作,因为gdb绑定了pid,会卡住,直到随着gdb的调试过程,执行到真正插入动作的函数
我在gdb端加了总共四个断点
二、执行到exec_simple_query()
先从当前位置开始连续运行程序,到第一个断点,从下往上看运行的堆栈:
可以看到,insert动作从main()函数开始,首先到了postmaster进程入口 PostmasterMain() ,通过ServerLoop() 监听session连接并fork postgres子进程 ,然后用BackendStartup()启动backend进程 ,之后到PostgresMain() 即backend的入口,通过子进程backend获取sql语句 ,并最终到了exec_simple_query() 即SQL引擎的入口。
可以在此时查看传入这里的参数
单步执行,查看执行过程中相关变量的值。
三、执行到ProcessQuery()
c继续执行,在下一个断点,即ProcessQuery()这里停下,查看堆栈,可以看到这一部分跑到了PortalRun()的入口,Portal是查询执行器的四个主要子模块之一,也通常被叫做策略选择模块,在这选择执行策略后,会将控制流程交给相应的处理部件,即Executer或者ProcessUtility。这里根据策略调用了PortalRunMulti() ,最后到达了ProcessQuery()。
我们继续单步执行,ProcessQuery()在这一部分创建QueryDesc,它封装了执行器执行查询所需的所有内容 ,调用ExecutorStart函数初始化结构体EState ,ExecutorStart函数调用InitPlan初始化计划状态树 。
这一部分执行流程过程如黄色所示:
四、执行到standard_ExecutorRun()
继续c执行,到了standard_ExecutorRun(),这里是先通过ExecutorRun ()这里进行判断,如果有hook函数,就执行hook函数,没有的话,执行标准函数standard_ExecutorRun()。(比较典型的使用hook的是pg_show_plans插件)
继续执行,最后通过standard_ExecutorRun()的ExecutePlan()执行insert并通过MemoryContextSwitchTo()切换回原内存上下文。
c继续执行一直到结束
可以在数据库端看到数据成功插入
整个插入动作的堆栈如下: