APPEND Plan Node节点的初始化和执行逻辑
发表于:2024-12-13 作者:千家信息网编辑
千家信息网最后更新 2024年12月13日,这篇文章主要为大家展示了"APPEND Plan Node节点的初始化和执行逻辑",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"APPEND Plan No
千家信息网最后更新 2024年12月13日APPEND Plan Node节点的初始化和执行逻辑
这篇文章主要为大家展示了"APPEND Plan Node节点的初始化和执行逻辑",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"APPEND Plan Node节点的初始化和执行逻辑"这篇文章吧。
一、数据结构
AppendState
用于Append Node执行的数据结构
/* ---------------- * AppendState information * AppendState结构体 * * nplans how many plans are in the array * 数组大小 * whichplan which plan is being executed (0 .. n-1), or a * special negative value. See nodeAppend.c. * 那个计划将要被/正在被执行(0 .. n-1),或者一个特别的负数,详见nodeAppend.c * pruningstate details required to allow partitions to be * eliminated from the scan, or NULL if not possible. * 在扫描过程中允许忽略分区的细节信息,入不可能则为NULL * valid_subplans for runtime pruning, valid appendplans indexes to * scan. * 用于运行期裁剪分区,将要扫描的有效appendplans索引 * ---------------- */struct AppendState;typedef struct AppendState AppendState;struct ParallelAppendState;typedef struct ParallelAppendState ParallelAppendState;struct PartitionPruneState;struct AppendState{ //第一个字段为NodeTag PlanState ps; /* its first field is NodeTag */ //PlanStates数组 PlanState **appendplans; /* array of PlanStates for my inputs */ //数组大小 int as_nplans; //那个计划将要被/正在被执行 int as_whichplan; //包含第一个部分计划的appendplans数组编号 int as_first_partial_plan; /* Index of 'appendplans' containing * the first partial plan */ //并行执行的协调信息 ParallelAppendState *as_pstate; /* parallel coordination info */ //协调信息大小 Size pstate_len; /* size of parallel coordination info */ //分区裁剪信息 struct PartitionPruneState *as_prune_state; //有效的子计划位图集 Bitmapset *as_valid_subplans; //选择下个子计划的实现函数 bool (*choose_next_subplan) (AppendState *);};
二、源码解读
ExecInitAppend
ExecInitAppend函数为开始append node的所有子关系扫描执行相关的初始化.
/* ---------------------------------------------------------------- * ExecInitAppend * * Begin all of the subscans of the append node. * 开始append node的所有子关系扫描 * * (This is potentially wasteful, since the entire result of the * append node may not be scanned, but this way all of the * structures get allocated in the executor's top level memory * block instead of that of the call to ExecAppend.) * (这可能是一种浪费,因为可能不会扫描append节点的整个结果, * 但是通过这种方式,所有结构都将在执行程序的最顶级内存块中分配, * 而不是在ExecAppend调用的内存块中分配。) * ---------------------------------------------------------------- */AppendState *ExecInitAppend(Append *node, EState *estate, int eflags){ AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; Bitmapset *validsubplans; int nplans; int firstvalid; int i, j; ListCell *lc; /* check for unsupported flags */ //检查不支持的标记 Assert(!(eflags & EXEC_FLAG_MARK)); /* * create new AppendState for our append node * 为append节点创建AppendState */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->ps.ExecProcNode = ExecAppend; /* Let choose_next_subplan_* function handle setting the first subplan */ //让choose_next_subplan_* 函数处理第一个子计划的设置 appendstate->as_whichplan = INVALID_SUBPLAN_INDEX; /* If run-time partition pruning is enabled, then set that up now */ //如允许运行时分区裁剪,则配置分区裁剪 if (node->part_prune_info != NULL) { PartitionPruneState *prunestate; /* We may need an expression context to evaluate partition exprs */ //需要独立的表达式上下文来解析分区表达式 ExecAssignExprContext(estate, &appendstate->ps); /* Create the working data structure for pruning. */ //为裁剪创建工作数据结构信息 prunestate = ExecCreatePartitionPruneState(&appendstate->ps, node->part_prune_info); appendstate->as_prune_state = prunestate; /* Perform an initial partition prune, if required. */ //如需要,执行初始的分区裁剪 if (prunestate->do_initial_prune) { /* Determine which subplans survive initial pruning */ //确定那个子计划在初始裁剪中仍"存活" validsubplans = ExecFindInitialMatchingSubPlans(prunestate, list_length(node->appendplans)); /* * The case where no subplans survive pruning must be handled * specially. The problem here is that code in explain.c requires * an Append to have at least one subplan in order for it to * properly determine the Vars in that subplan's targetlist. We * sidestep this issue by just initializing the first subplan and * setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that * we don't really need to scan any subnodes. * 没有子计划"存活"的情况需要特别处理. * 这里的问题是explain.c的执行逻辑至少需要一个Append节点和一个子计划, * 用于确定子计划投影列链表中的Vars. * 通过初始化第一个子计划以及设置as_whichplan为NO_MATCHING_SUBPLANS, * 用以指示不需要扫描任何的子节点. */ if (bms_is_empty(validsubplans)) { appendstate->as_whichplan = NO_MATCHING_SUBPLANS; /* Mark the first as valid so that it's initialized below */ //标记第一个子计划为有效的,以便在接下来可以初始化 validsubplans = bms_make_singleton(0); } //子计划数目 nplans = bms_num_members(validsubplans); } else { /* We'll need to initialize all subplans */ //需要初始化所有的的子计划 nplans = list_length(node->appendplans); Assert(nplans > 0); validsubplans = bms_add_range(NULL, 0, nplans - 1); } /* * If no runtime pruning is required, we can fill as_valid_subplans * immediately, preventing later calls to ExecFindMatchingSubPlans. * 如不需要运行期裁剪,那么我们可以马上设置as_valid_subplans, * 以防止后续逻辑调用ExecFindMatchingSubPlans */ if (!prunestate->do_exec_prune) { Assert(nplans > 0); appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1); } } else { //不需要运行期裁剪 nplans = list_length(node->appendplans); /* * When run-time partition pruning is not enabled we can just mark all * subplans as valid; they must also all be initialized. * 运行期裁剪不允许,设置所有的子计划为有效,同时这些子计划必须全部初始化 */ Assert(nplans > 0); appendstate->as_valid_subplans = validsubplans = bms_add_range(NULL, 0, nplans - 1); appendstate->as_prune_state = NULL; } /* * Initialize result tuple type and slot. * 初始化结果元组类型和slot */ ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual); /* node returns slots from each of its subnodes, therefore not fixed */ //从每一个子节点中返回slot,但并不固定 appendstate->ps.resultopsset = true; appendstate->ps.resultopsfixed = false; appendplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *)); /* * call ExecInitNode on each of the valid plans to be executed and save * the results into the appendplanstates array. * 在每一个有效的计划上执行ExecInitNode, * 同时保存结果到appendplanstates数组中. * * While at it, find out the first valid partial plan. * 在此期间,找到第一个有效的部分计划(并行使用) */ j = i = 0; firstvalid = nplans; foreach(lc, node->appendplans) { if (bms_is_member(i, validsubplans)) { Plan *initNode = (Plan *) lfirst(lc); /* * Record the lowest appendplans index which is a valid partial * plan. * 记录有效部分计划的最小appendplans索引号 */ if (i >= node->first_partial_plan && j < firstvalid) firstvalid = j; appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);//初始化节点 } i++; } //配置Append State appendstate->as_first_partial_plan = firstvalid; appendstate->appendplans = appendplanstates; appendstate->as_nplans = nplans; /* * Miscellaneous initialization * 初始化 */ appendstate->ps.ps_ProjInfo = NULL; /* For parallel query, this will be overridden later. */ //对于并行查询,该值后面会被覆盖 appendstate->choose_next_subplan = choose_next_subplan_locally; return appendstate;}
ExecAppend
ExecAppend在多个子计划中进行迭代处理
/* ---------------------------------------------------------------- * ExecAppend * * Handles iteration over multiple subplans. * 在多个子计划中处理迭代 * ---------------------------------------------------------------- */static TupleTableSlot *ExecAppend(PlanState *pstate){ AppendState *node = castNode(AppendState, pstate); if (node->as_whichplan < 0) { /* * If no subplan has been chosen, we must choose one before * proceeding. * 如果没有子计划选中,必须在开始前选择一个 */ if (node->as_whichplan == INVALID_SUBPLAN_INDEX && !node->choose_next_subplan(node)) return ExecClearTuple(node->ps.ps_ResultTupleSlot);//出错,则清除tuple,返回 /* Nothing to do if there are no matching subplans */ //如无匹配的子计划,返回 else if (node->as_whichplan == NO_MATCHING_SUBPLANS) return ExecClearTuple(node->ps.ps_ResultTupleSlot); } for (;;)//循环获取tuple slot { PlanState *subnode; TupleTableSlot *result; CHECK_FOR_INTERRUPTS(); /* * figure out which subplan we are currently processing * 选择哪个子计划需要现在执行 */ Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans); subnode = node->appendplans[node->as_whichplan]; /* * get a tuple from the subplan * 从子计划中获取tuple */ result = ExecProcNode(subnode); if (!TupIsNull(result)) { /* * If the subplan gave us something then return it as-is. We do * NOT make use of the result slot that was set up in * ExecInitAppend; there's no need for it. * 如果子计划有返回,则直接返回. * 在这里,不需要在ExecInitAppend中构造结果slot,因为不需要这样做. */ return result; } /* choose new subplan; if none, we're done */ //选择新的子计划,如无,则完成调用 if (!node->choose_next_subplan(node)) return ExecClearTuple(node->ps.ps_ResultTupleSlot); }}
三、跟踪分析
测试脚本如下
testdb=# explain verbose select * from t_hash_partition where c1 = 1 OR c1 = 2; QUERY PLAN ------------------------------------------------------------------------------------- Append (cost=0.00..30.53 rows=6 width=200) -> Seq Scan on public.t_hash_partition_1 (cost=0.00..15.25 rows=3 width=200) Output: t_hash_partition_1.c1, t_hash_partition_1.c2, t_hash_partition_1.c3 Filter: ((t_hash_partition_1.c1 = 1) OR (t_hash_partition_1.c1 = 2)) -> Seq Scan on public.t_hash_partition_3 (cost=0.00..15.25 rows=3 width=200) Output: t_hash_partition_3.c1, t_hash_partition_3.c2, t_hash_partition_3.c3 Filter: ((t_hash_partition_3.c1 = 1) OR (t_hash_partition_3.c1 = 2))(7 rows)
ExecInitAppend
启动gdb,设置断点
(gdb) b ExecInitAppendBreakpoint 1 at 0x6efa8a: file nodeAppend.c, line 103.(gdb) cContinuing.Breakpoint 1, ExecInitAppend (node=0x27af638, estate=0x27be058, eflags=16) at nodeAppend.c:103103 AppendState *appendstate = makeNode(AppendState);
初始化AppendState
(gdb) n113 Assert(!(eflags & EXEC_FLAG_MARK));(gdb) 119 ExecLockNonLeafAppendTables(node->partitioned_rels, estate);(gdb) 124 appendstate->ps.plan = (Plan *) node;(gdb) 125 appendstate->ps.state = estate;(gdb) 126 appendstate->ps.ExecProcNode = ExecAppend;(gdb) 129 appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;(gdb)
不需要运行期分区裁剪
(gdb) 132 if (node->part_prune_info != NULL)(gdb) p node->part_prune_info$1 = (struct PartitionPruneInfo *) 0x0(gdb)
需要初始化所有的的子计划
(gdb) n190 nplans = list_length(node->appendplans);(gdb) 196 Assert(nplans > 0);(gdb) 198 bms_add_range(NULL, 0, nplans - 1);(gdb) 197 appendstate->as_valid_subplans = validsubplans =(gdb) n199 appendstate->as_prune_state = NULL;(gdb) p *validsubplans$4 = {nwords = 1, words = 0x27be38c}(gdb) p *validsubplans->words$5 = 3 --> 即No.0 + No.1(gdb)
初始化结果元组类型和slot
(gdb) n205 ExecInitResultTupleSlotTL(estate, &appendstate->ps);(gdb) 207 appendplanstates = (PlanState **) palloc(nplans *(gdb)
在每一个有效的计划上执行ExecInitNode,同时保持结果到appendplanstates数组中.
(gdb) 216 j = i = 0;(gdb) n217 firstvalid = nplans;(gdb) 218 foreach(lc, node->appendplans)(gdb) p nplans$6 = 2(gdb) p node->appendplans$7 = (List *) 0x27b30f0(gdb) p *node->appendplans$8 = {type = T_List, length = 2, head = 0x27b30c8, tail = 0x27b33d8}(gdb)
遍历appendplans,初始化appendplans中的节点(SeqScan)
(gdb) n220 if (bms_is_member(i, validsubplans))(gdb) n222 Plan *initNode = (Plan *) lfirst(lc);(gdb) 228 if (i >= node->first_partial_plan && j < firstvalid)(gdb) 231 appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);(gdb) p j$9 = 0(gdb) p i$10 = 0(gdb) n233 i++;(gdb) 218 foreach(lc, node->appendplans)(gdb) 220 if (bms_is_member(i, validsubplans))(gdb) 222 Plan *initNode = (Plan *) lfirst(lc);(gdb) 228 if (i >= node->first_partial_plan && j < firstvalid)(gdb) p *initNode$11 = {type = T_SeqScan, startup_cost = 0, total_cost = 15.25, plan_rows = 3, plan_width = 200, parallel_aware = false, parallel_safe = true, plan_node_id = 2, targetlist = 0x27b31a8, qual = 0x27b3308, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}(gdb) n231 appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);(gdb) 233 i++;(gdb) 218 foreach(lc, node->appendplans)(gdb) 236 appendstate->as_first_partial_plan = firstvalid;(gdb)
完成初始化,其中choose_next_subplan函数为choose_next_subplan_locally函数
(gdb) p firstvalid$12 = 2(gdb) n237 appendstate->appendplans = appendplanstates;(gdb) 238 appendstate->as_nplans = nplans;(gdb) 244 appendstate->ps.ps_ProjInfo = NULL;(gdb) 247 appendstate->choose_next_subplan = choose_next_subplan_locally;(gdb) 249 return appendstate;(gdb) p choose_next_subplan_locally$13 = {_Bool (AppendState *)} 0x6f02d8(gdb) p *appendstate$15 = {ps = {type = T_AppendState, plan = 0x27af638, state = 0x27be058, ExecProcNode = 0x6efe19 , ExecProcNodeReal = 0x0, instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x27be5c0, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x0}, appendplans = 0x27be6b8, as_nplans = 2, as_whichplan = -1, as_first_partial_plan = 2, as_pstate = 0x0, pstate_len = 0, as_prune_state = 0x0, as_valid_subplans = 0x27be388, choose_next_subplan = 0x6f02d8 }
ExecAppend
设置断点,进入ExecAppend
(gdb) del Delete all breakpoints? (y or n) y(gdb) b ExecAppendBreakpoint 2 at 0x6efe2a: file nodeAppend.c, line 261.(gdb) cContinuing.Breakpoint 2, ExecAppend (pstate=0x27be270) at nodeAppend.c:261261 AppendState *node = castNode(AppendState, pstate);
输入参数为先前已完成初始化的AppendState
(gdb) p *(AppendState *)pstate$19 = {ps = {type = T_AppendState, plan = 0x27af638, state = 0x27be058, ExecProcNode = 0x6efe19, ExecProcNodeReal = 0x6efe19 , instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x27be5c0, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, scandesc = 0x0}, appendplans = 0x27be6b8, as_nplans = 2, as_whichplan = -1, as_first_partial_plan = 2, as_pstate = 0x0, pstate_len = 0, as_prune_state = 0x0, as_valid_subplans = 0x27be388, choose_next_subplan = 0x6f02d8 }
如果没有子计划选中,必须在开始前选择一个(选择子计划:No.0)
(gdb) n263 if (node->as_whichplan < 0)(gdb) 269 if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&(gdb) 270 !node->choose_next_subplan(node))(gdb) 269 if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&(gdb) 274 else if (node->as_whichplan == NO_MATCHING_SUBPLANS)(gdb) p node->as_whichplan$20 = 0(gdb) n283 CHECK_FOR_INTERRUPTS();
获取子计划并执行
(gdb) 288 Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);(gdb) 289 subnode = node->appendplans[node->as_whichplan];(gdb) 294 result = ExecProcNode(subnode);(gdb) p *subnode$21 = {type = T_SeqScanState, plan = 0x27b2728, state = 0x27be058, ExecProcNode = 0x6e4bde, ExecProcNodeReal = 0x71578d , instrument = 0x0, worker_instrument = 0x0, worker_jit_instrument = 0x0, qual = 0x27bec88, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x27bebc8, ps_ExprContext = 0x27be7f8, ps_ProjInfo = 0x0, scandesc = 0x7f7d049417e0}
返回slot
(gdb) n296 if (!TupIsNull(result))(gdb) p *result$22 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x27c5500, tts_tupleDescriptor = 0x7f7d049417e0, tts_mcxt = 0x27bdf40, tts_buffer = 120, tts_nvalid = 1, tts_values = 0x27be950, tts_isnull = 0x27be968, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = { bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 4, tts_fixedTupleDescriptor = true}
如第一个子计划执行完毕,则选择下一个子计划(调用choose_next_subplan函数切换到下一个子计划)
(gdb) cContinuing.Breakpoint 2, ExecAppend (pstate=0x27be270) at nodeAppend.c:261261 AppendState *node = castNode(AppendState, pstate);(gdb) n263 if (node->as_whichplan < 0)(gdb) 283 CHECK_FOR_INTERRUPTS();(gdb) 288 Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);(gdb) 289 subnode = node->appendplans[node->as_whichplan];(gdb) 294 result = ExecProcNode(subnode);(gdb) 296 if (!TupIsNull(result))(gdb) 307 if (!node->choose_next_subplan(node))(gdb) 309 }
以上是"APPEND Plan Node节点的初始化和执行逻辑"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
个子
节点
有效
函数
数组
结果
选择
逻辑
信息
结构
行期
内容
同时
大小
数据
数据结构
篇文章
部分
处理
内存
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
农村宅基地制度改革数据库设计
服务器系统盘与数据盘
拨打对方号码显示服务器出错
2021颁布的网络安全法律
怎么把数据库开启远程连接
网络技术部宗旨
南航网络安全意识考试
国外服务器哪家好
闵行区常规软件开发服务保障
fm2022数据库更新后开档
安全数据库 制药
网络安全 加密算法
数据库中怎么判断关键字
软件开发台式机推荐
服务器系统恢复指令
上海游驰网络技术 牛彦
偶像梦幻祭日服服务器
提供网络技术服务注册公司
数据库怎么显示最新的10条数据
网络安全的产生
csgo开箱开出网络安全
mssql数据库导入
网络技术员都用什么软件
徐州互联网智慧校园软件开发
手机软件开发工具软件
fm2022数据库更新后开档
AMB是什么服务器
什么是数据库的收缩和压缩
软件开发技能视频教程
炮兵远火营侦察兵和网络安全专业