工程咨询公司加盟合作,小璇seo优化网站,做外贸哪些网站可以发布产品,秦皇岛公司做网站执行算子#xff08;Append算子#xff09; Append 算子ExecInitAppend 函数exec_append_initialize_next 函数ExecAppend 函数ExecEndAppend 函数ExecReScanAppend 函数 声明#xff1a;本文的部分内容参考了他人的文章。在编写过程中#xff0c;我们尊重他人的知识产权和… 执行算子Append算子 Append 算子ExecInitAppend 函数exec_append_initialize_next 函数ExecAppend 函数ExecEndAppend 函数ExecReScanAppend 函数 声明本文的部分内容参考了他人的文章。在编写过程中我们尊重他人的知识产权和学术成果力求遵循合理使用原则并在适用的情况下注明引用来源。 本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书、《PostgresSQL数据库内核分析》一书以及相关学习资料。 Append 算子 Append 算子对应的代码源文件是 “nodeAppend.cpp”用于处理包含一个或多个子计划的链表。Append 算子遍历子计划链表逐个执行子计划当子计划返回全部结果后迭代执行下一个子计划。Append 算子通常用于 SQL 中的集合操作中例如多个 Union All 操作可以对多个子查询的结果取并集另外 Append 算子还可以用来实现继承表的查询功能。 以下是对 Append 算子的一些详细描述 多子计划处理Append 算子通过遍历包含一个或多个子计划的链表逐个执行这些子计划。每个子计划对应一个子查询这些子查询的结果将按照它们在链表中的顺序合并输出。顺序合并子计划的执行是按照它们在链表中的顺序进行的。当一个子计划返回全部结果后Append 算子迭代执行下一个子计划。这确保了最终输出结果的顺序与子计划在链表中的顺序一致。用途主要用于处理 SQL 查询中的集合操作例如多个 UNION ALL 操作。在这种情况下每个子查询可能对应于一个分支或条件而 Append 算子将这些分支的结果合并为一个整体结果。继承表查询 Append 算子还常用于实现继承表的查询功能。在面向对象的数据库设计中可能存在一个包含所有子类的继承表而每个子类对应一个子查询。Append 算子可以将这些子查询的结果按顺序组合形成包含所有子类数据的查询结果。执行计划灵活性 Append 算子的存在使得查询优化器能够以更灵活的方式处理复杂的查询结构。通过将多个子计划组合成一个逻辑单元优化器可以更好地进行成本估算和执行计划选择。 Append算子对应的主要函数如下表所示
主要函数说 明ExecInitAppend初始化 Append 节点ExecAppend迭代获取元组ExecEndAppend关闭 Append 节点ExecReScanAppend重新扫描 Append 节点exec_append_initialize_next为下一个扫描节点设置状态 按照传统下面我们还是以一个案例来调试一下代码吧首先执行以下 sql 语句
-- 创建表
CREATE TABLE employees (emp_id SERIAL PRIMARY KEY,emp_name VARCHAR(100),emp_department VARCHAR(50)
);-- 插入数据
INSERT INTO employees (emp_name, emp_department) VALUES(John Doe, HR),(Alice Smith, IT),(Bob Johnson, Finance);-- 第一个子查询
SELECT emp_id, emp_name, emp_department FROM employees WHERE emp_department HR
UNION ALL
-- 第二个子查询
SELECT emp_id, emp_name, emp_department FROM employees WHERE emp_department IT
UNION ALL
-- 第三个子查询
SELECT emp_id, emp_name, emp_department FROM employees WHERE emp_department Finance;在这个例子中我们通过 UNION ALL 连接了三个子查询每个子查询选择了属于不同部门的雇员。这样的查询可以模拟 Append 算子的效果将来自不同子查询的结果合并为一个整体结果。请注意这只是一种模拟实际的 Append 算子是由数据库系统内部生成的。
ExecInitAppend 函数 ExecInitAppend 函数是为 Append 节点执行计划初始化阶段而设计的。在初始化过程中它创建了 AppendState 结构体用于存储 Append 节点的执行状态信息包括子计划的执行状态数组、当前执行的子计划编号等。接着它遍历 Append 节点的子计划列表对每个子计划调用 ExecInitNode 进行初始化并将结果保存到数组中。此外还初始化了执行结果存储槽和输出元组类型。最终函数返回初始化完成的 AppendState 结构体为执行 Append 节点提供了必要的状态和信息。在执行阶段该结构体将被用于跟踪和管理 Append 节点的执行过程。函数源码如下所示路径src/gausskernel/runtime/executor/nodeAppend.cpp
/* ----------------------------------------------------------------* ExecInitAppend** 开始执行所有的追加节点子计划。** 这可能是不够高效的因为追加节点的整个结果可能不会被扫描* 但这样所有的结构都会被分配在执行器的顶级内存块中而不是* 在对 ExecAppend 的调用的内存块中。* ----------------------------------------------------------------*/
AppendState* ExecInitAppend(Append* node, EState* estate, int eflags)
{// 创建 AppendState 结构体用于存储追加节点的执行状态信息AppendState* appendstate makeNode(AppendState);// 子计划状态数组PlanState** appendplanstates;// 子计划数量int nplans;// 循环变量int i;ListCell* lc NULL;// 检查是否存在不支持的标志Assert(!(eflags EXEC_FLAG_MARK));/** 设置子计划状态的空向量*/// 获取子计划的数量nplans list_length(node-appendplans);// 分配存储子计划状态的数组appendplanstates (PlanState**)palloc0(nplans * sizeof(PlanState*));/** 为追加节点创建新的 AppendState*/// 初始化 AppendState 结构体的基本信息appendstate-ps.plan (Plan*)node;appendstate-ps.state estate;appendstate-appendplans appendplanstates;appendstate-as_nplans nplans;/** 杂项初始化** 追加计划没有表达式上下文因为它们从不调用 ExecQual 或 ExecProject。*/// 初始化执行上下文因为追加节点不涉及 ExecQual 或 ExecProject/** 追加节点仍然具有 Result 存储槽其中保存了指向元组的指针因此* 我们必须对它们进行初始化。*/// 初始化 Result 存储槽用于存储指向元组的指针ExecInitResultTupleSlot(estate, appendstate-ps);/** 对要执行的每个子计划调用 ExecInitNode并将结果保存到数组“appendplans”中。*/// 遍历子计划列表对每个子计划调用 ExecInitNode 进行初始化i 0;foreach (lc, node-appendplans) {Plan* initNode (Plan*)lfirst(lc);// 执行初始化并保存结果到数组中appendplanstates[i] ExecInitNode(initNode, estate, eflags);i;}/** 初始化输出元组类型* 追加的 Result 元组槽始终包含一个虚拟元组* 该槽的默认表AM类型为 Heap。*/// 初始化输出元组类型ExecAssignResultTypeFromTL(appendstate-ps, TAM_HEAP);appendstate-ps.ps_ProjInfo NULL;/** 初始化以扫描第一个子计划*/// 初始化当前执行的子计划编号appendstate-as_whichplan 0;// 执行初始化下一个子计划的操作(void)exec_append_initialize_next(appendstate);// 返回初始化完成的 AppendState 结构体为执行追加节点提供必要的状态和信息return appendstate;
}函数的调用关系如下所示
#0 ExecInitAppend (node0x7f15adf87f18, estate0x7f15ae6aa060, eflags16) at nodeAppend.cpp:112
#1 0x000000000159910a in ExecInitNodeByType (node0x7f15adf87f18, estate0x7f15ae6aa060, eflags16) at execProcnode.cpp:262
#2 0x0000000001599bf0 in ExecInitNode (node0x7f15adf87f18, estate0x7f15ae6aa060, e_flags16) at execProcnode.cpp:497
#3 0x0000000001607b33 in ExecInitResult (node0x7f15adf87868, estate0x7f15ae6aa060, eflags16) at nodeResult.cpp:239
#4 0x00000000015990d4 in ExecInitNodeByType (node0x7f15adf87868, estate0x7f15ae6aa060, eflags16) at execProcnode.cpp:258
#5 0x0000000001599bf0 in ExecInitNode (node0x7f15adf87868, estate0x7f15ae6aa060, e_flags16) at execProcnode.cpp:497
#6 0x00000000015939ba in InitPlan (queryDesc0x7f15ae47f860, eflags16) at execMain.cpp:1437
#7 0x0000000001591404 in standard_ExecutorStart (queryDesc0x7f15ae47f860, eflags16) at execMain.cpp:382
#8 0x00007f160055e78a in gs_audit_executor_start_hook (queryDesc0x7f15ae47f860, eflags0) at gs_policy_plugin.cpp:1907
#9 0x000000000139a43d in explain_ExecutorStart (queryDesc0x7f15ae47f860, eflags0) at auto_explain.cpp:83
#10 0x0000000001590e1b in ExecutorStart (queryDesc0x7f15ae47f860, eflags0) at execMain.cpp:228
#11 0x0000000001470c03 in PortalStart (portal0x7f15ae4ce060, params0x0, eflags0, snapshot0x0) at pquery.cpp:784
#12 0x000000000145d184 in exec_simple_query (
---Type return to continue, or q return to quit---函数的调式信息如下所示
(gdb) p nplans
$1 3
(gdb) p *appendstate
$2 {ps {type T_AppendState, plan 0x7f15adf87f18, state 0x7f15ae6aa060, instrument 0x0, targetlist 0x0, qual 0x0, lefttree 0x0,righttree 0x0, initPlan 0x0, subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x0, ps_ExprContext 0x0,ps_ProjInfo 0x0, ps_TupFromTlist false, vectorized false, nodeContext 0x0, earlyFreed false, stubType 0 \000, jitted_vectarget 0x0,plan_issues 0x0, recursive_reset false, qual_is_inited false, ps_rownum 0}, appendplans 0x7f15ae738188, as_nplans 3, as_whichplan 0}
(gdb) p *appendstate-ps.plan
$3 {type T_Append, plan_node_id 2, parent_node_id 1, exec_type EXEC_ON_DATANODES, startup_cost 0, total_cost 38.317499999999995, plan_rows 3,multiple 0, plan_width 340, dop 1, pred_rows -1, pred_startup_time -1, pred_total_time -1, pred_max_memory -1, recursive_union_plan_nodeid 0,recursive_union_controller false, control_plan_nodeid 0, is_sync_plannode false, targetlist 0x7f15adf88140, qual 0x0, lefttree 0x0,righttree 0x0, ispwj false, paramno -1, initPlan 0x0, distributed_keys 0x0, exec_nodes 0x7f15ae56dd40, extParam 0x0, allParam 0x0,vec_output false, hasUniqueResults false, isDeltaTable false, operatorMemKB {0, 0}, operatorMaxMem 0, parallel_enabled false,hasHashFilter false, var_list 0x0, filterIndexList 0x0, ng_operatorMemKBArray 0x0, ng_num 0, innerdistinct 1, outerdistinct 1}
(gdb) p *appendstate-ps.state
$4 {type T_EState, es_direction ForwardScanDirection, es_snapshot 0x7f15ae46c4f8, es_crosscheck_snapshot 0x0, es_range_table 0x7f15adf8a6a0,es_plannedstmt 0x7f15adf8d728, es_junkFilter 0x0, es_output_cid 0, es_result_relations 0x0, es_num_result_relations 0,es_result_relation_info 0x0, esCurrentPartition 0x0, esfRelations 0x0, es_result_remoterel 0x0, es_result_insert_remoterel 0x0,es_result_update_remoterel 0x0, es_result_delete_remoterel 0x0, es_trig_target_relations 0x0, es_trig_tuple_slot 0x0, es_trig_oldtup_slot 0x0,es_trig_newtup_slot 0x0, es_param_list_info 0x0, es_param_exec_vals 0x0, es_query_cxt 0x7f15ae542b28, es_const_query_cxt 0x7f15ae543a40,es_tupleTable 0x7f15ae5445d0, es_rowMarks 0x0, es_processed 0, es_last_processed 0, es_lastoid 0, es_top_eflags 16, es_instrument 0,es_finished false, es_exprcontexts 0x7f15ae5443f8, es_subplanstates 0x0, es_auxmodifytables 0x0, es_remotequerystates 0x0,es_per_tuple_exprcontext 0x0, es_epqTuple 0x0, es_epqTupleSet 0x0, es_epqScanDone 0x0, es_subplan_ids 0x0, es_skip_early_free false,es_skip_early_deinit_consumer false, es_under_subplan false, es_material_of_subplan 0x0, es_recursive_next_iteration false, dataDestRelIndex 0,es_bloom_filter {bfarray 0x0, array_size 0}, es_can_realtime_statistics false, es_can_history_statistics false, isRowTriggerShippable false}
(gdb) p appendstate-as_nplans
$5 3
(gdb) p **appendplanstates2
$14 {{type T_SeqScanState, plan 0x7f15adf885a0, state 0x7f15ae6aa060, instrument 0x0, targetlist 0x7f15ae54c710, qual 0x7f15ae54d948,lefttree 0x0, righttree 0x0, initPlan 0x0, subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x7f15ae54ce88,ps_ExprContext 0x7f15ae54c288, ps_ProjInfo 0x0, ps_TupFromTlist false, vectorized false, nodeContext 0x7f15ae543c68, earlyFreed false,stubType 0 \000, jitted_vectarget 0x0, plan_issues 0x0, recursive_reset false, qual_is_inited true, ps_rownum 0}, {type 2927208672,plan 0x7f15ade6c860, state 0x7f15ae54cff8, instrument 0x0, targetlist 0x0, qual 0x0, lefttree 0x0, righttree 0x0, initPlan 0x0,subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x0, ps_ExprContext 0x0, ps_ProjInfo 0x0, ps_TupFromTlist false,vectorized false, nodeContext 0x0, earlyFreed false, stubType 0 \000, jitted_vectarget 0x160acad SeqNext(SeqScanState*),plan_issues 0x7f15ae543c68, recursive_reset false, qual_is_inited 2, ps_rownum -6510615555426900571}}exec_append_initialize_next 函数 exec_append_initialize_next 函数用于设置追加节点状态以准备进行下一个子计划的扫描。该函数检查当前迭代的子计划索引是否在有效范围内如果是则返回 true表示有下一个子计划需要处理如果超出索引范围则根据扫描的方向正向或逆向进行调整然后返回 false通知 ExecAppend 函数已经到达了子计划列表的末尾。这个函数在追加节点执行过程中被调用用于初始化追加节点状态以准备处理下一个子计划。函数源码如下所示路径src/gausskernel/runtime/executor/nodeAppend.cpp
/* ----------------------------------------------------------------* exec_append_initialize_next** 设置追加状态节点以进行“下一个”扫描。** 如果有“下一个”扫描要处理则返回 true。* ----------------------------------------------------------------*/
bool exec_append_initialize_next(AppendState* appendstate)
{int whichplan;/** 从追加节点获取信息*/whichplan appendstate-as_whichplan;if (whichplan 0) {/** 如果是逆向扫描我们从列表中的最后一个扫描开始* 然后向前进行到第一个.. 在任何情况下通过返回 FALSE* 通知 ExecAppend 我们已经到达了行的末尾*/appendstate-as_whichplan 0;return FALSE;} else if (whichplan appendstate-as_nplans) {/** 如上如果超出了列表中的最后一个扫描结束扫描..*/appendstate-as_whichplan appendstate-as_nplans - 1;return FALSE;} else {// 如果存在下一个扫描则返回 truereturn TRUE;}
}函数的调式信息如下所示
(gdb) p whichplan
$1 0
(gdb) p appendstate-as_nplans
$2 3因此whichplan appendstate-as_nplans 条件成立返回 TURE。
ExecAppend 函数 ExecAppend 函数负责处理对多个子计划进行迭代的逻辑。它循环遍历子计划从当前子计划获取元组如果获取到元组则直接返回否则释放当前子计划的资源并切换到下一个子计划继续循环。在正向或逆向扫描中如果没有更多的子计划可供处理则返回一个由 ExecInitAppend 设置的空槽。这样ExecAppend 实现了对多个子计划的迭代执行用于支持像多个 Union All 操作这样的集合操作以及继承表的查询等功能。函数源码如下所示路径src/gausskernel/runtime/executor/nodeAppend.cpp
/* ----------------------------------------------------------------* ExecAppend** 处理多个子计划的迭代。* ----------------------------------------------------------------*/
TupleTableSlot* ExecAppend(AppendState* node) {for (;;) {PlanState* subnode NULL; // 当前处理的子计划节点TupleTableSlot* result NULL; // 子计划的执行结果/** 确定当前正在处理的子计划*/subnode node-appendplans[node-as_whichplan];/** 从子计划获取一个元组*/result ExecProcNode(subnode);if (!TupIsNull(result)) {/** 如果子计划返回了结果则直接返回。我们不使用在* ExecInitAppend 中设置的结果槽没有必要使用它。*/return result;}/* 提前释放每个子计划的资源 */ExecEarlyFree(subnode);/** 转到适当方向上的“下一个”子计划。如果没有更多的子计划则返回* 由 ExecInitAppend 为我们设置的空槽。*/if (ScanDirectionIsForward(node-ps.state-es_direction))node-as_whichplan;elsenode-as_whichplan--;if (!exec_append_initialize_next(node))return ExecClearTuple(node-ps.ps_ResultTupleSlot);/* 否则回到循环顶部尝试从新的子计划获取元组 */}
}函数的调用关系如下所示
#0 ExecAppend (node0x7f15ae738060) at nodeAppend.cpp:185
#1 0x000000000159a253 in ExecProcNodeByType (node0x7f15ae738060) at execProcnode.cpp:609
#2 0x000000000159a8dd in ExecProcNode (node0x7f15ae738060) at execProcnode.cpp:769
#3 0x0000000001607807 in ExecResult (node0x7f15ae544060) at nodeResult.cpp:124
#4 0x000000000159a231 in ExecProcNodeByType (node0x7f15ae544060) at execProcnode.cpp:604
#5 0x000000000159a8dd in ExecProcNode (node0x7f15ae544060) at execProcnode.cpp:769
#6 0x0000000001595232 in ExecutePlan (estate0x7f15ae6aa060, planstate0x7f15ae544060, operationCMD_SELECT, sendTuplestrue, numberTuples0,directionForwardScanDirection, dest0x7f15adf30318) at execMain.cpp:2124
#7 0x0000000001591d6a in standard_ExecutorRun (queryDesc0x7f15ae47f860, directionForwardScanDirection, count0) at execMain.cpp:608
#8 0x000000000139a5d4 in explain_ExecutorRun (queryDesc0x7f15ae47f860, directionForwardScanDirection, count0) at auto_explain.cpp:116
#9 0x000000000159188f in ExecutorRun (queryDesc0x7f15ae47f860, directionForwardScanDirection, count0) at execMain.cpp:484
#10 0x000000000147298f in PortalRunSelect (portal0x7f15ae4ce060, forwardtrue, count0, dest0x7f15adf30318) at pquery.cpp:1396
#11 0x0000000001471b5c in PortalRun (portal0x7f15ae4ce060, count9223372036854775807, isTopLeveltrue, dest0x7f15adf30318, altdest0x7f15adf30318,
---Type return to continue, or q return to quit---函数的调式信息如下所示
(gdb) p *subnode
$1 {type T_SeqScanState, plan 0x7f15adf885a0, state 0x7f15ae6aa060, instrument 0x0, targetlist 0x7f15ae54c710, qual 0x7f15ae54d948,lefttree 0x0, righttree 0x0, initPlan 0x0, subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x7f15ae54ce88,ps_ExprContext 0x7f15ae54c288, ps_ProjInfo 0x0, ps_TupFromTlist false, vectorized false, nodeContext 0x7f15ae543c68, earlyFreed false,stubType 0 \000, jitted_vectarget 0x0, plan_issues 0x0, recursive_reset false, qual_is_inited true, ps_rownum 0}
(gdb) p result
$2 (TupleTableSlot *) 0x7f15ae54cff8
(gdb) p *result
$3 {type T_TupleTableSlot, tts_isempty false, tts_shouldFree false, tts_shouldFreeMin false, tts_slow true, tts_tuple 0x7f15ade6cb28,tts_dataRow 0x0, tts_dataLen -1, tts_shouldFreeRow false, tts_attinmeta 0x0, tts_xcnodeoid 0, tts_per_tuple_mcxt 0x7f15ae6a0f78,tts_tupleDescriptor 0x7f15ae793348, tts_mcxt 0x7f15ae543c68, tts_buffer 151, tts_nvalid 3, tts_values 0x7f15ae54d9b0, tts_isnull 0x7f15ae54da18,tts_mintuple 0x0, tts_minhdr {tupTableType 0 \000, t_bucketId 0, t_len 0, t_self {ip_blkid {bi_hi 0, bi_lo 0}, ip_posid 0},t_tableOid 0, t_xid_base 0, t_multi_base 0, t_xc_node_id 0, t_data 0x0}, tts_off 16, tts_meta_off 0, tts_tupslotTableAm TAM_HEAP}
(gdb) p *result.tts_values
$4 1
(gdb) p *result.tts_isnull
$5 falseExecEndAppend 函数 ExecEndAppend 函数用于关闭 Append 节点的子扫描。它首先获取 AppendState 结构中的子计划数组和计划数量信息然后遍历关闭每个子计划通过调用 ExecEndNode 函数完成关闭操作。这样ExecEndAppend 负责释放 Append 节点相关的资源确保在执行结束时进行清理。函数源码如下所示路径src/gausskernel/runtime/executor/nodeAppend.cpp
/* ----------------------------------------------------------------* ExecEndAppend** Shuts down the subscans of the append node.** Returns nothing of interest.* ----------------------------------------------------------------*/
void ExecEndAppend(AppendState* node)
{// 获取子计划数组和计划数量信息PlanState** appendplans node-appendplans;int nplans node-as_nplans;int i;/** shut down each of the subscans* 逐个关闭每个子计划*/for (i 0; i nplans; i)ExecEndNode(appendplans[i]); // 调用ExecEndNode函数完成关闭操作
}函数的调用关系如下所示
#0 ExecEndAppend (node0x7f15ae738060) at nodeAppend.cpp:242
#1 0x000000000159c01e in ExecEndNodeByType (node0x7f15ae738060) at execProcnode.cpp:1084
#2 0x000000000159c5d6 in ExecEndNode (node0x7f15ae738060) at execProcnode.cpp:1374
#3 0x0000000001607bd4 in ExecEndResult (node0x7f15ae544060) at nodeResult.cpp:279
#4 0x000000000159bffc in ExecEndNodeByType (node0x7f15ae544060) at execProcnode.cpp:1075
#5 0x000000000159c5d6 in ExecEndNode (node0x7f15ae544060) at execProcnode.cpp:1374
#6 0x0000000001594cfa in ExecEndPlan (planstate0x7f15ae544060, estate0x7f15ae6aa060) at execMain.cpp:1910
#7 0x0000000001592232 in standard_ExecutorEnd (queryDesc0x7f15ae47f860) at execMain.cpp:766
#8 0x00000000014954f2 in pgaudit_ExecutorEnd (queryDesc0x7f15ae47f860) at auditfuncs.cpp:1600
#9 0x00000000014d98be in hypo_executorEnd_hook (queryDesc0x7f15ae47f860) at hypopg_index.cpp:216
#10 0x00000000015920bb in ExecutorEnd (queryDesc0x7f15ae47f860) at execMain.cpp:714
#11 0x00000000012bc705 in PortalCleanup (portal0x7f15ae4ce060) at portalcmds.cpp:280
#12 0x0000000000e68985 in PortalDrop (portal0x7f15ae4ce060, isTopCommitfalse) at portalmem.cpp:498
---Type return to continue, or q return to quit---函数的调式信息如下所示
(gdb) p **appendplans
$1 {type T_SeqScanState, plan 0x7f15adf885a0, state 0x7f15ae6aa060, instrument 0x0, targetlist 0x7f15ae54c710, qual 0x7f15ae54d948,lefttree 0x0, righttree 0x0, initPlan 0x0, subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x7f15ae54ce88,ps_ExprContext 0x7f15ae54c288, ps_ProjInfo 0x0, ps_TupFromTlist false, vectorized false, nodeContext 0x7f15ae543c68, earlyFreed true,stubType 0 \000, jitted_vectarget 0x0, plan_issues 0x0, recursive_reset false, qual_is_inited true, ps_rownum 2}
执行 ExecEndNode 函数后结果如下
(gdb) p **appendplans
$33 {type T_SeqScanState, plan 0x7f15adf885a0, state 0x7f15ae6aa060, instrument 0x0, targetlist 0x7f15ae54c710, qual 0x7f15ae54d948,lefttree 0x0, righttree 0x0, initPlan 0x0, subPlan 0x0, chgParam 0x0, hbktScanSlot {currSlot 0}, ps_ResultTupleSlot 0x7f15ae54ce88,ps_ExprContext 0x0, ps_ProjInfo 0x0, ps_TupFromTlist false, vectorized false, nodeContext 0x7f15ae543c68, earlyFreed true, stubType 0 \000,jitted_vectarget 0x0, plan_issues 0x0, recursive_reset false, qual_is_inited true, ps_rownum 2}ExecReScanAppend 函数 ExecReScanAppend 函数用于重新启动 Append 节点的扫描过程。它遍历 Append 节点的所有子计划对每个子计划执行重新扫描操作。在重新扫描前函数检查是否有参数变更如果有则通过 UpdateChangedParamSet 函数进行变更参数的信号处理。如果子计划的 chgParam 不为 null表示需要在后续的 ExecProcNode 中重新扫描该子计划。最后函数重置 Append 节点的当前子计划索引并调用exec_append_initialize_next 函数重新初始化 Append 节点的状态以准备开始新的扫描过程。函数源码如下所示路径src/gausskernel/runtime/executor/nodeAppend.cpp
void ExecReScanAppend(AppendState* node)
{int i;for (i 0; i node-as_nplans; i) {PlanState* subnode node-appendplans[i];/** ExecReScan doesnt know about my subplans, so I have to do* changed-parameter signaling myself.* ExecReScan不了解我的子计划因此我必须自己进行参数变更信号处理。*/if (node-ps.chgParam ! NULL)UpdateChangedParamSet(subnode, node-ps.chgParam);/** If chgParam of subnode is not null then plan will be re-scanned by* first ExecProcNode.* 如果subnode的chgParam不为null则计划将在第一个ExecProcNode中重新扫描。*/if (subnode-chgParam NULL)ExecReScan(subnode); // 调用ExecReScan函数重新扫描子计划}node-as_whichplan 0;(void)exec_append_initialize_next(node); // 重新初始化Append节点的状态
}ExecReScanAppend 函数通常在执行计划需要重新扫描的时候被调用。这可能是由于外部参数的变化需要重新执行整个计划。例如考虑以下情况 假设有一个包含多个子查询的 Append 节点每个子查询对应于不同的分区。如果在执行计划期间某个影响所有子查询结果的外部参数发生变化为了保证计划的正确性就需要调用 ExecReScanAppend。例如
-- 创建一个包含多个子查询的Append计划
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM table1 WHERE column 1
UNION ALL
SELECT * FROM table2 WHERE column 2
UNION ALL
SELECT * FROM table3 WHERE column 3;假设在上述计划执行过程中外部条件 column 的值发生变化为了反映这个变化就需要调用 ExecReScanAppend 重新扫描所有子查询确保计划的执行结果是最新的。