iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >APPEND Plan Node节点的初始化和执行逻辑
  • 819
分享到

APPEND Plan Node节点的初始化和执行逻辑

2024-04-02 19:04:59 819人浏览 八月长安
摘要

这篇文章主要为大家展示了“APPEND Plan node节点的初始化和执行逻辑”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“APPEND Plan Node节

这篇文章主要为大家展示了“APPEND Plan node节点的初始化和执行逻辑”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“APPEND Plan Node节点的初始化和执行逻辑”这篇文章吧。

一、数据结构

AppendState
用于Append Node执行的数据结构



struct AppendState;
typedef struct AppendState AppendState;
struct ParallelAppendState;
typedef struct ParallelAppendState ParallelAppendState;
struct PartitionPruneState;

struct AppendState
{
    //第一个字段为NodeTag
    PlanState   ps;             
    //PlanStates数组
    PlanState **appendplans;    
    //数组大小
    int         as_nplans;
    //那个计划将要被/正在被执行
    int         as_whichplan;
    //包含第一个部分计划的appendplans数组编号
    int         as_first_partial_plan;  
    //并行执行的协调信息
    ParallelAppendState *as_pstate; 
    //协调信息大小
    Size        pstate_len;     
    //分区裁剪信息
    struct PartitionPruneState *as_prune_state;
    //有效的子计划位图集
    Bitmapset  *as_valid_subplans;
    //选择下个子计划的实现函数
    bool        (*choose_next_subplan) (AppendState *);
};

二、源码解读

ExecInitAppend
ExecInitAppend函数为开始append node的所有子关系扫描执行相关的初始化.


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;

    
    //检查不支持的标记
    Assert(!(eflags & EXEC_FLAG_MARK));

    
    appendstate->ps.plan = (Plan *) node;
    appendstate->ps.state = estate;
    appendstate->ps.ExecProcNode = ExecAppend;

    
    //让choose_next_subplan_* 函数处理第一个子计划的设置
    appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;

    
    //如允许运行时分区裁剪,则配置分区裁剪
    if (node->part_prune_info != NULL)
    {
        PartitionPruneState *prunestate;

        
        //需要独立的表达式上下文来解析分区表达式
        ExecAssignExprContext(estate, &appendstate->ps);

        
        //为裁剪创建工作数据结构信息
        prunestate = ExecCreatePartitionPruneState(&appendstate->ps,
                                                   node->part_prune_info);
        appendstate->as_prune_state = prunestate;

        
        //如需要,执行初始的分区裁剪
        if (prunestate->do_initial_prune)
        {
            
            //确定那个子计划在初始裁剪中仍"存活"
            validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
                                                            list_length(node->appendplans));

            
            if (bms_is_empty(validsubplans))
            {
                appendstate->as_whichplan = NO_MATCHING_SUBPLANS;

                
                //标记第一个子计划为有效的,以便在接下来可以初始化
                validsubplans = bms_make_singleton(0);
            }
            //子计划数目
            nplans = bms_num_members(validsubplans);
        }
        else
        {
            
            //需要初始化所有的的子计划
            nplans = list_length(node->appendplans);
            Assert(nplans > 0);
            validsubplans = bms_add_range(NULL, 0, nplans - 1);
        }

        
        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);

        
        Assert(nplans > 0);
        appendstate->as_valid_subplans = validsubplans =
            bms_add_range(NULL, 0, nplans - 1);
        appendstate->as_prune_state = NULL;
    }

    
    ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);

    
    //从每一个子节点中返回slot,但并不固定
    appendstate->ps.resultopsset = true;
    appendstate->ps.resultopsfixed = false;

    appendplanstates = (PlanState **) palloc(nplans *
                                             sizeof(PlanState *));

    
    j = i = 0;
    firstvalid = nplans;
    foreach(lc, node->appendplans)
    {
        if (bms_is_member(i, validsubplans))
        {
            Plan       *initNode = (Plan *) lfirst(lc);

            
            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;

    

    appendstate->ps.ps_ProjInfo = NULL;

    
    //对于并行查询,该值后面会被覆盖
    appendstate->choose_next_subplan = choose_next_subplan_locally;

    return appendstate;
}

ExecAppend
ExecAppend在多个子计划中进行迭代处理


static TupleTableSlot *
ExecAppend(PlanState *pstate)
{
    AppendState *node = castNode(AppendState, pstate);

    if (node->as_whichplan < 0)
    {
        
        if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
            !node->choose_next_subplan(node))
            return ExecClearTuple(node->ps.ps_ResultTupleSlot);//出错,则清除tuple,返回

        
        //如无匹配的子计划,返回
        else if (node->as_whichplan == NO_MATCHING_SUBPLANS)
            return ExecClearTuple(node->ps.ps_ResultTupleSlot);
    }

    for (;;)//循环获取tuple slot
    {
        PlanState  *subnode;
        TupleTableSlot *result;

        CHECK_FOR_INTERRUPTS();

        
        Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);
        subnode = node->appendplans[node->as_whichplan];

        
        result = ExecProcNode(subnode);

        if (!TupIsNull(result))
        {
            
            return result;
        }

        
        //选择新的子计划,如无,则完成调用
        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 ExecInitAppend
Breakpoint 1 at 0x6efa8a: file nodeAppend.c, line 103.
(gdb) c
Continuing.

Breakpoint 1, ExecInitAppend (node=0x27af638, estate=0x27be058, eflags=16) at nodeAppend.c:103
103     AppendState *appendstate = makeNode(AppendState);

初始化AppendState

(gdb) n
113     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) n
190         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) n
199         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) n
205     ExecInitResultTupleSlotTL(estate, &appendstate->ps);
(gdb) 
207     appendplanstates = (PlanState **) palloc(nplans *
(gdb)

在每一个有效的计划上执行ExecInitNode,同时保持结果到appendplanstates数组中.

(gdb) 
216     j = i = 0;
(gdb) n
217     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) n
220         if (bms_is_member(i, validsubplans))
(gdb) n
222             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) n
233         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) n
231             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) n
237     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 <choose_next_subplan_locally>
(gdb) p *appendstate
$15 = {ps = {type = T_AppendState, plan = 0x27af638, state = 0x27be058, ExecProcNode = 0x6efe19 <ExecAppend>, 
    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 <choose_next_subplan_locally>}

ExecAppend
设置断点,进入ExecAppend

(gdb) del 
Delete all breakpoints? (y or n) y
(gdb) b ExecAppend
Breakpoint 2 at 0x6efe2a: file nodeAppend.c, line 261.
(gdb) c
Continuing.

Breakpoint 2, ExecAppend (pstate=0x27be270) at nodeAppend.c:261
261     AppendState *node = castNode(AppendState, pstate);

输入参数为先前已完成初始化的AppendState

(gdb) p *(AppendState *)pstate
$19 = {ps = {type = T_AppendState, plan = 0x27af638, state = 0x27be058, ExecProcNode = 0x6efe19 <ExecAppend>, 
    ExecProcNodeReal = 0x6efe19 <ExecAppend>, 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 <choose_next_subplan_locally>}

如果没有子计划选中,必须在开始前选择一个(选择子计划:No.0)

(gdb) n
263     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) n
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) p *subnode
$21 = {type = T_SeqScanState, plan = 0x27b2728, state = 0x27be058, ExecProcNode = 0x6e4bde <ExecProcNodeFirst>, 
  ExecProcNodeReal = 0x71578d <ExecSeqScan>, 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) n
296         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) c
Continuing.

Breakpoint 2, ExecAppend (pstate=0x27be270) at nodeAppend.c:261
261     AppendState *node = castNode(AppendState, pstate);
(gdb) n
263     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节点的初始化和执行逻辑”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网数据库频道!

您可能感兴趣的文档:

--结束END--

本文标题: APPEND Plan Node节点的初始化和执行逻辑

本文链接: https://www.lsjlt.com/news/64076.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • APPEND Plan Node节点的初始化和执行逻辑
    这篇文章主要为大家展示了“APPEND Plan Node节点的初始化和执行逻辑”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“APPEND Plan Node节...
    99+
    2024-04-02
  • Java子类和父类的初始化执行顺序
      要明白子类和父类的初始化执行顺序,只需要知晓以下三点,就不会再弄错了。  1.创建子类对象时,子类和父类的静态块和构造方法的执行顺序为:父类静态块->子类静态块->父类构造器->子类构造器。深入理解为什么是这个顺序,可...
    99+
    2023-06-02
  • PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点
    这篇文章主要介绍“PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点”,在日常操作中,相信很多人在PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点问题上存在...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作