- 专栏内容:postgresql内核源码分析
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
总体流程
调用堆栈
执行接口说明
详细流程分解
ExecInsert对于普通表的insert;
插入tuple的流程,调用table_tuple_insert(heapam_tuple_insert)中;
heap_insert的流程:
索引创建过程简述
结尾
前言
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。
总体流程
(1)如果是分区表,需要找到对应子表;
(2)待插入表是否有索引,需要打开对应的索引表;
(3)处理before row insert triggers
(4)处理instead of row insert triggers
(5)如果是Fdw(foreign-data wrapper)接口的表,单独处理
(6)处理tuple检查和insert buffer
(7)如果是分区表,引起分区key变化,导致有tuple要移动到新分区时,处理数据的delete/insert;
(8)处理after row insert triggers
(9) 处理with check option;
(10) 生成returning
调用堆栈
ExecInsert(ModifyTableState * mtstate, ResultRelInfo * resultRelInfo, TupleTableSlot * slot, TupleTableSlot * planSlot, EState * estate, _Bool canSetTag)
ExecModifyTable(PlanState * pstate)
ExecProcNode(PlanState * node)
ExecutePlan(_Bool execute_once, DestReceiver * dest, ScanDirection direction, uint64 numberTuples, CmdType operation, _Bool use_parallel_mode, PlanState * planstate, EState * estate)
standard_ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once)
ProcessQuery(PlannedStmt * plan, const char * sourceText, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
exec_simple_query(const char * query_string)
PostgresMain(const char * dbname, const char * username)
BackendRun()
BackendStartup()
ServerLoop()
PostmasterMain(int argc, char ** argv)
main(int argc, char ** argv)
在经过SQL解析,生成执行plan后,就开始调用PortalRun进行执行阶段,最后调用ExecutePlan,按照plan中的每个node进行执行,不同的node有不同的类型,比如merge/sort等等(详细参见本专栏的执行计划分享),每种类型有对应的执行调用,对于insert的执行动作,主要由ExecInsert来完成。
执行接口说明
static TupleTableSlot *
ExecInsert(ModifyTableContext *context,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
bool canSetTag,
TupleTableSlot **inserted_tuple,
ResultRelInfo **insert_destrel)
slot,中存储有需要insert的tuple;
**inserted_tuple,插入成功后的tuple;
**inserted_destrel,是出参,新tuple插入成功后的表,因为这里有可能是不同的分区表;
插入不成功函数返回值为NULL。
详细流程分解
postgresql在整个处理时,进行分层处理,这里涉及到了两层:
一是执行层,对应的调用是ExecInsert
二是heapam层,也就是针对heap类型表的操作;这里是为了支持多种类型表而留了扩展设计的空间;heapam层对应的调用是table_tuple_insert
-
ExecInsert对于普通表的insert;
(1)如果有自动生成列,进行数据生成;
(2)with check option类型判断和处理;
(3)检查约束
(4)检查分区表约束,是否符合分区条件;
(5)是否有冲突处理策略,如果有需要检查处理;
(6)如果没有冲突处理策略,则正常插入tuple和创建索引;
-
插入tuple的流程,调用table_tuple_insert(heapam_tuple_insert)中;
(1)从slot记录的tuple数据,拼装成要插入的tuple;
(2)获取table OID
(3)调用heap_insert,查找空闲空间将tuple插入page,记录wal等;
(4)将插入的tuple的tid记录到slot;方便后面进行索引的插入;
(5)如果有申请tuple空间,在这里释放;
-
heap_insert的流程:
(1)准备tuple,在拼装好的tuple上继续填写infomask/xmin/xmax等;对于可压缩的,当前tup大于toast存储阈值时,要拼成toast存储tuple;
(2)查找有空闲空间的buffer来插入当前tuple;同时检查是否都可见,标记vm文件;(查找空闲空间的流程请查看本专栏的文章)
(3)检查串行化级别的冲突;
(4)开启关键代码区;将tuple放到buffer中;
(5)更新可见性信息;
(6)标脏buffer;
(7)写WAL日志;结束关键代码区;
(8)释放buffer,vmbuffer;
(9)如果插入的是系统表的tuple,需要invalidatecache;
(10)记录t_cid给返回者,释放申请的tuple;
-
索引创建过程简述
(1)在tuple插入成功后,得到tuple的tid;
(2)调用ExecInsertIndexTuples,执行indextuple的插入;这是在执行层的调用;
(3)每张表可以有多个index,所以扫描数据字典,查找有多少个索引;然后每个索引进行分别插入;
(4)对于每个索引,调用对应的Indexam层接口进行处理;这里调用index_insert,它内部调用indexRelation->rd_indam->aminsert,这是在创建索引时初始化好的。索引相关详细内容查看本专栏的内容。
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!