我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PostgreSQL 源码解读(98)- 分区表#4(数据查询路由#1-“扩展”分区表)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PostgreSQL 源码解读(98)- 分区表#4(数据查询路由#1-“扩展”分区表)

在查询分区表的时候PG如何确定查询的是哪个分区?如何确定?相关的机制是什么?接下来几个章节将一一介绍,本节是第一部分。

零、实现机制

我们先看下面的例子,两个普通表t_normal_1和t_normal_2,执行UNION ALL操作:

drop table if exists t_normal_1;
drop table if exists t_normal_2;
create table t_normal_1 (c1 int not null,c2  varchar(40),c3 varchar(40));
create table t_normal_2 (c1 int not null,c2  varchar(40),c3 varchar(40));

insert into t_normal_1(c1,c2,c3) VALUES(0,'HASH0','HAHS0');
insert into t_normal_2(c1,c2,c3) VALUES(0,'HASH0','HAHS0');

testdb=# explain verbose select * from t_normal_1 where c1 = 0
testdb-# union all
testdb-# select * from t_normal_2 where c1 <> 0;
                                 QUERY PLAN                                 
----------------------------------------------------------------------------
 Append  (cost=0.00..34.00 rows=350 width=200)
   ->  Seq Scan on public.t_normal_1  (cost=0.00..14.38 rows=2 width=200)
         Output: t_normal_1.c1, t_normal_1.c2, t_normal_1.c3
         Filter: (t_normal_1.c1 = 0)
   ->  Seq Scan on public.t_normal_2  (cost=0.00..14.38 rows=348 width=200)
         Output: t_normal_2.c1, t_normal_2.c2, t_normal_2.c3
         Filter: (t_normal_2.c1 <> 0)
(7 rows)

两张普通表的UNION ALL,PG使用APPEND操作符把t_normal_1顺序扫描的结果集和t_normal_2顺序扫描的结果集"APPEND"在一起作为最终的结果集输出.

分区表的查询也是类似的机制,把各个分区的结果集APPEND在一起,然后作为最终的结果集输出,如下例所示:

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)

查询分区表t_hash_partition,条件为c1 = 1 OR c1 = 2,从执行计划可见是把t_hash_partition_1顺序扫描的结果集和t_hash_partition_3顺序扫描的结果集"APPEND"在一起作为最终的结果集输出.

这里面有几个问题需要解决:
1.识别分区表并找到所有的分区子表;
2.根据约束条件识别需要查询的分区,这是出于性能的考虑;
3.对结果集执行APPEND,作为最终结果输出.
本节介绍了PG如何识别分区表并找到所有的分区子表,实现的函数是expand_inherited_tables.

一、数据结构

AppendRelInfo
Append-relation信息.
当我们将可继承表(分区表)或UNION-ALL子查询展开为“追加关系”(本质上是子RTE的链表)时,为每个子RTE构建一个AppendRelInfo。
AppendRelInfos链表指示在展开父节点时必须包含哪些子rte,每个节点具有将引用父节点的Vars转换为引用该子节点的Vars所需的所有信息。



typedef struct AppendRelInfo
{
    NodeTag     type;

    
    Index       parent_relid;   
    Index       child_relid;    

    
    Oid         parent_reltype; 
    Oid         child_reltype;  

    
    //child's Vars中的表达式
    List       *translated_vars;    

    
    Oid         parent_reloid;  
} AppendRelInfo;

PlannerInfo
该数据结构用于存储查询语句在规划/优化过程中的相关信息


struct AppendRelInfo;

typedef struct PlannerInfo
{
    NodeTag     type;//Node标识
    //查询树
    Query      *parse;          
    //当前的planner全局信息
    PlannerGlobal *glob;        
    //查询层次,1标识最高层
    Index       query_level;    
    // 如为子计划,则这里存储父计划器指针,NULL标识最高层
    struct PlannerInfo *parent_root;    

    
    List       *plan_params;    
    Bitmapset  *outer_params;

    
    //RelOptInfo数组,存储"base rels",比如基表/子查询等.
    //该数组与RTE的顺序一一对应,而且是从1开始,因此[0]无用 */
    struct RelOptInfo **simple_rel_array;   
    int         simple_rel_array_size;  

    
    //RTE数组
    RangeTblEntry **simple_rte_array;   

    
    //处理集合操作如UNION ALL时使用和分区表时使用
    struct AppendRelInfo **append_rel_array;

    
    Relids      all_baserels;//"base rels"

    
    //Nullable-side端的"base rels"
    Relids      nullable_baserels;

    
    //参与连接的Relation的RelOptInfo链表
    List       *join_rel_list;  
    //可加快链表访问的hash表
    struct HTAB *join_rel_hash; 

    
    //RelOptInfo指针链表数组,k层的join存储在[k]中
    List      **join_rel_level; 
    //当前的join层次
    int         join_cur_level; 
    //查询的初始化计划链表
    List       *init_plans;     
    //CTE子计划ID链表
    List       *cte_plan_ids;   
    //MULTIEXPR子查询输出的参数链表的链表
    List       *multiexpr_params;   
    //活动的等价类链表
    List       *eq_classes;     
    //规范化的PathKey链表
    List       *canon_pathkeys; 
    //外连接约束条件链表(左)
    List       *left_join_clauses;  
    //外连接约束条件链表(右)
    List       *right_join_clauses; 
    //全连接约束条件链表
    List       *full_join_clauses;  
    //特殊连接信息链表
    List       *join_info_list; 
    //AppendRelInfo链表
    List       *append_rel_list;    
    //PlanRowMarks链表
    List       *rowMarks;       
    //PHI链表
    List       *placeholder_list;   
    // 外键信息链表
    List       *fkey_list;      
    //query_planner()要求的PathKeys链表
    List       *query_pathkeys; 
    //分组子句路径键
    List       *group_pathkeys; 
    //窗口函数路径键
    List       *window_pathkeys;    
    //distinctClause路径键
    List       *distinct_pathkeys;  
    //排序路径键
    List       *sort_pathkeys;  
    //已规范化的分区Schema
    List       *part_schemes;   
    //尝试连接的RelOptInfo链表
    List       *initial_rels;   

    
    //上层的RelOptInfo链表
    List       *upper_rels[UPPERREL_FINAL + 1]; 

    
    //grouping_planner为上层处理选择的结果tlists
    struct PathTarget *upper_targets[UPPERREL_FINAL + 1];//

    
    ////最后需处理的投影列
    List       *processed_tlist;

    
    //setrefs.c中在create_plan()函数调用期间填充的字段
    //分组函数属性映射
    AttrNumber *grouping_map;   
    //MinMaxAggInfos链表
    List       *minmax_aggs;    
    //内存上下文
    MemoryContext planner_cxt;  
    //关系的page计数
    double      total_table_pages;  
    //query_planner输入参数:元组处理比例
    double      tuple_fraction; 
    //query_planner输入参数:limit_tuple
    double      limit_tuples;   
    //表达式的最小安全等级
    Index       qual_security_level;    
    
    //注意:如果没有securityQuals, 则qual_security_level是NULL(0)

    //如目标relation是分区表的child/partition/分区表,则通过此字段标记
    InheritanceKind inhTargetKind;  
    //是否存在RTE_JOIN的RTE
    bool        hasJoinRTEs;    
    //是否存在标记为LATERAL的RTE
    bool        hasLateralRTEs; 
    //是否存在已在jointree删除的RTE
    bool        hasDeletedRTEs; 
    //是否存在Having子句
    bool        hasHavingQual;  
    //如约束条件中存在pseudoconstant = true,则此字段为T
    bool        hasPseudoConstantQuals; 
    //是否存在递归语句
    bool        hasRecursion;   

    
    //这些字段仅在hasRecursion为T时使用:
    //工作表的PARAM_EXEC ID
    int         wt_param_id;    
    //非递归模式的访问路径
    struct Path *non_recursive_path;    

    
    //这些字段用于createplan.c
    //当前节点之上的外部rels
    Relids      curOuterRels;   
    //未赋值的NestLoopParams参数
    List       *curOuterParams; 

    
    //可选的join_search_hook私有数据,例如GEQO
    void       *join_search_private;

    
    //该查询是否更新分区键列?
    bool        partColsUpdated;
} PlannerInfo;

二、源码解读

expand_inherited_tables函数将表示继承集合的每个范围表条目展开为“append relation”。


void
expand_inherited_tables(PlannerInfo *root)
{
    Index       nrtes;
    Index       rti;
    ListCell   *rl;

    
    nrtes = list_length(root->parse->rtable);
    rl = list_head(root->parse->rtable);
    for (rti = 1; rti <= nrtes; rti++)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);

        expand_inherited_rtentry(root, rte, rti);
        rl = lnext(rl);
    }
}


static void
expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
{
    Oid         parentOID;
    PlanRowMark *oldrc;
    Relation    oldrelation;
    LOCKMODE    lockmode;
    List       *inhOIDs;
    ListCell   *l;

    
    //是否分区表?
    if (!rte->inh)
        return;
    
    //忽略所有已扩展的UNION ALL节点
    if (rte->rtekind != RTE_RELATION)
    {
        Assert(rte->rtekind == RTE_SUBQUERY);
        return;//返回
    }
    
    //对于常规的无子表的关系,快速判断
    parentOID = rte->relid;
    if (!has_subclass(parentOID))
    {
        
        //无子表,设置标记并返回
        rte->inh = false;
        return;
    }

    
    lockmode = rte->rellockmode;

    
    //扫描继承集的所有成员,获取所需的锁
    inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);

    
    if (list_length(inhOIDs) < 2)
    {
        
        //清除标记,返回
        rte->inh = false;
        return;
    }

    
    oldrc = get_plan_rowmark(root->rowMarks, rti);
    if (oldrc)
        oldrc->isParent = true;

    
    oldrelation = heap_open(parentOID, NoLock);

    
    //扫描继承集合并扩展之
    if (RelationGetPartitionDesc(oldrelation) != NULL)//
    {
        Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);

        
        expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
                                   lockmode, &root->append_rel_list);
    }
    else
    {
        //分区描述符获取不成功(没有分区信息)
        List       *appinfos = NIL;
        RangeTblEntry *childrte;
        Index       childRTindex;

        
        foreach(l, inhOIDs)//遍历OIDs
        {
            Oid         childOID = lfirst_oid(l);
            Relation    newrelation;

            
            //如有需要,打开rel(已获得锁)
            if (childOID != parentOID)
                newrelation = heap_open(childOID, NoLock);
            else
                newrelation = oldrelation;

            
            if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
            {
                heap_close(newrelation, lockmode);//忽略它们
                continue;
            }

            expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
                                            newrelation,
                                            &appinfos, &childrte,
                                            &childRTindex);//展开

            
            //关闭子表,但仍持有锁
            if (childOID != parentOID)
                heap_close(newrelation, NoLock);
        }

        
        if (list_length(appinfos) < 2)
            rte->inh = false;//设置标记
        else
            root->append_rel_list = list_concat(root->append_rel_list,
                                                appinfos);//添加到链表中

    }

    heap_close(oldrelation, NoLock);//关闭relation
}


static void
expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
                           Index parentRTindex, Relation parentrel,
                           PlanRowMark *top_parentrc, LOCKMODE lockmode,
                           List **appinfos)
{
    int         i;
    RangeTblEntry *childrte;
    Index       childRTindex;
    PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);

    check_stack_depth();

    
    //分配表通常应具备分区描述符
    Assert(partdesc);

    Assert(parentrte->inh);

    
    if (!root->partColsUpdated)
        root->partColsUpdated =
            has_partition_attrs(parentrel, parentrte->updatedCols, NULL);

    
    //
    expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
                                    top_parentrc, parentrel,
                                    appinfos, &childrte, &childRTindex);

    
    if (partdesc->nparts == 0)
    {
        parentrte->inh = false;
        return;
    }

    for (i = 0; i < partdesc->nparts; i++)
    {
        Oid         childOID = partdesc->oids[i];
        Relation    childrel;

        
        //打开rel
        childrel = heap_open(childOID, NoLock);

        
        if (RELATION_IS_OTHER_TEMP(childrel))
            elog(ERROR, "temporary relation from another session found as partition");
        //扩展之
        expand_single_inheritance_child(root, parentrte, parentRTindex,
                                        parentrel, top_parentrc, childrel,
                                        appinfos, &childrte, &childRTindex);

        
        //子关系是分区表,递归扩展
        if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
            expand_partitioned_rtentry(root, childrte, childRTindex,
                                       childrel, top_parentrc, lockmode,
                                       appinfos);

        
        //关闭子关系,但仍持有锁
        heap_close(childrel, NoLock);
    }
}


 
static void
expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
                                Index parentRTindex, Relation parentrel,
                                PlanRowMark *top_parentrc, Relation childrel,
                                List **appinfos, RangeTblEntry **childrte_p,
                                Index *childRTindex_p)
{
    Query      *parse = root->parse;
    Oid         parentOID = RelationGetRelid(parentrel);//父关系
    Oid         childOID = RelationGetRelid(childrel);//子关系
    RangeTblEntry *childrte;
    Index       childRTindex;
    AppendRelInfo *appinfo;

    
    childrte = copyObject(parentrte);
    *childrte_p = childrte;
    childrte->relid = childOID;
    childrte->relkind = childrel->rd_rel->relkind;
    
    //分区表的子关系会在"将来"扩展
    if (childOID != parentOID &&
        childrte->relkind == RELKIND_PARTITIONED_TABLE)
        childrte->inh = true;
    else
        childrte->inh = false;
    childrte->requiredPerms = 0;
    childrte->securityQuals = NIL;
    parse->rtable = lappend(parse->rtable, childrte);
    childRTindex = list_length(parse->rtable);
    *childRTindex_p = childRTindex;

    
    if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
    {
        appinfo = makeNode(AppendRelInfo);
        appinfo->parent_relid = parentRTindex;
        appinfo->child_relid = childRTindex;
        appinfo->parent_reltype = parentrel->rd_rel->reltype;
        appinfo->child_reltype = childrel->rd_rel->reltype;
        make_inh_translation_list(parentrel, childrel, childRTindex,
                                  &appinfo->translated_vars);
        appinfo->parent_reloid = parentOID;
        *appinfos = lappend(*appinfos, appinfo);

        
        if (childOID != parentOID)
        {
            childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
                                                         appinfo->translated_vars);
            childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
                                                         appinfo->translated_vars);
            childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
                                                        appinfo->translated_vars);
        }
    }

    
    if (top_parentrc)
    {
        PlanRowMark *childrc = makeNode(PlanRowMark);

        childrc->rti = childRTindex;
        childrc->prti = top_parentrc->rti;
        childrc->rowmarkId = top_parentrc->rowmarkId;
        
        //重新选择rowmark类型,因为relkind可能与父类不匹配
        childrc->markType = select_rowmark_type(childrte,
                                                top_parentrc->strength);
        childrc->allMarkTypes = (1 << childrc->markType);
        childrc->strength = top_parentrc->strength;
        childrc->waitPolicy = top_parentrc->waitPolicy;

        
        childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);

        
        //在父类的allMarkTypes中包含子类的rowmark类型
        top_parentrc->allMarkTypes |= childrc->allMarkTypes;

        root->rowMarks = lappend(root->rowMarks, childrc);
    }
}

三、跟踪分析

测试脚本如下

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)

启动gdb,设置断点

(gdb) b expand_inherited_tables
Breakpoint 1 at 0x7e53ba: file prepunion.c, line 1483.
(gdb) c
Continuing.

Breakpoint 1, expand_inherited_tables (root=0x28fcdc8) at prepunion.c:1483
1483        nrtes = list_length(root->parse->rtable);

获取RTE的个数和链表元素

(gdb) n
1484        rl = list_head(root->parse->rtable);
(gdb) 
1485        for (rti = 1; rti <= nrtes; rti++)
(gdb) p nrtes
$1 = 1
(gdb) p *rl
$2 = {data = {ptr_value = 0x28d83d0, int_value = 42828752, oid_value = 42828752}, next = 0x0}
(gdb) 

循环处理RTE

(gdb) n
1487            RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
(gdb) 
1489            expand_inherited_rtentry(root, rte, rti);
(gdb) p *rte
$3 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16986, relkind = 112 'p', tablesample = 0x0, subquery = 0x0, 
  security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, funcordinality = false, 
  tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, self_reference = false, coltypes = 0x0, 
  coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, enrtuples = 0, alias = 0x0, eref = 0x28d84e8, lateral = false, 
  inh = true, inFromCl = true, requiredPerms = 2, checkAsUser = 0, selectedCols = 0x28d8c40, insertedCols = 0x0, 
  updatedCols = 0x0, securityQuals = 0x0}

进入expand_inherited_rtentry

(gdb) step
expand_inherited_rtentry (root=0x28fcdc8, rte=0x28d83d0, rti=1) at prepunion.c:1517
1517        Query      *parse = root->parse;

expand_inherited_rtentry->分区表标记为T

1526        if (!rte->inh)
(gdb) p rte->inh
$4 = true

expand_inherited_rtentry->执行相关判断

(gdb) n
1529        if (rte->rtekind != RTE_RELATION)
(gdb) p rte->rtekind
$5 = RTE_RELATION
(gdb) n
1535        parentOID = rte->relid;
(gdb) 
1536        if (!has_subclass(parentOID))
(gdb) p parentOID
$6 = 16986
(gdb) n
1556        oldrc = get_plan_rowmark(root->rowMarks, rti);
(gdb) 
1557        if (rti == parse->resultRelation)
(gdb) p *oldrc
Cannot access memory at address 0x0

expand_inherited_rtentry->扫描继承集的所有成员,获取所需的锁,并构建OIDs链表

(gdb) n
1559        else if (oldrc && RowMarkRequiresRowShareLock(oldrc->markType))
(gdb) 
1562            lockmode = AccessShareLock;
(gdb) 
1565        inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
(gdb) 
1572        if (list_length(inhOIDs) < 2)
(gdb) p inhOIDs
$7 = (List *) 0x28fd208
(gdb) p *inhOIDs
$8 = {type = T_OidList, length = 7, head = 0x28fd1e0, tail = 0x28fd778}
(gdb) 

expand_inherited_rtentry->打开relation

(gdb) n
1584        if (oldrc)
(gdb) 
1591        oldrelation = heap_open(parentOID, NoLock);

expand_inherited_rtentry->成功获取分区描述符,调用expand_partitioned_rtentry

(gdb) 
1594        if (RelationGetPartitionDesc(oldrelation) != NULL)
(gdb) 
1596            Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
(gdb) 
1603            expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
(gdb) 

expand_inherited_rtentry->进入expand_partitioned_rtentry

(gdb) step
expand_partitioned_rtentry (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, parentrel=0x7f4e66827980, 
    top_parentrc=0x0, lockmode=1, appinfos=0x28fce98) at prepunion.c:1684
1684        PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);

expand_partitioned_rtentry->获取分区描述符

1684        PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
(gdb) n
1686        check_stack_depth();
(gdb) p *partdesc
$9 = {nparts = 6, oids = 0x298e4f8, boundinfo = 0x298e530}

expand_partitioned_rtentry->执行相关校验

(gdb) n
1689        Assert(partdesc);
(gdb) 
1691        Assert(parentrte->inh);
(gdb) 
1700        if (!root->partColsUpdated)
(gdb) 
1702                has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
(gdb) 
1701            root->partColsUpdated =
(gdb) 
1705        expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,

expand_partitioned_rtentry->首先展开分区表本身,进入expand_single_inheritance_child

(gdb) step
expand_single_inheritance_child (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, parentrel=0x7f4e66827980, 
    top_parentrc=0x0, childrel=0x7f4e66827980, appinfos=0x28fce98, childrte_p=0x7ffd1928d2f8, childRTindex_p=0x7ffd1928d2f4)
    at prepunion.c:1778
1778        Query      *parse = root->parse;

expand_single_inheritance_child->执行相关初始化(childrte)

(gdb) n
1779        Oid         parentOID = RelationGetRelid(parentrel);
(gdb) 
1780        Oid         childOID = RelationGetRelid(childrel);
(gdb) 
1797        childrte = copyObject(parentrte);
(gdb) p parentOID
$10 = 16986
(gdb) p childOID
$11 = 16986
(gdb) n
1798        *childrte_p = childrte;
(gdb) 
1799        childrte->relid = childOID;
(gdb) 
1800        childrte->relkind = childrel->rd_rel->relkind;
(gdb) 
1802        if (childOID != parentOID &&
(gdb) 
1806            childrte->inh = false;
(gdb) 
1807        childrte->requiredPerms = 0;
(gdb) 
1808        childrte->securityQuals = NIL;
(gdb) 
1809        parse->rtable = lappend(parse->rtable, childrte);
(gdb) 
1810        childRTindex = list_length(parse->rtable);
(gdb) 
1811        *childRTindex_p = childRTindex;
(gdb) p *childrte -->relid = 16986,仍为分区表
$12 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16986, relkind = 112 'p', tablesample = 0x0, subquery = 0x0, 
  security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, funcordinality = false, 
  tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, self_reference = false, coltypes = 0x0, 
  coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, enrtuples = 0, alias = 0x0, eref = 0x28fd268, lateral = false, 
  inh = false, inFromCl = true, requiredPerms = 0, checkAsUser = 0, selectedCols = 0x28fd898, insertedCols = 0x0, 
  updatedCols = 0x0, securityQuals = 0x0}
(gdb) p *childRTindex_p
$13 = 0

expand_single_inheritance_child->完成分区表本身的扩展,回到expand_partitioned_rtentry

(gdb) p *childRTindex_p
$13 = 0
(gdb) n
1820        if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
(gdb) 
1855        if (top_parentrc)
(gdb) 
1881    }
(gdb) 
expand_partitioned_rtentry (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, parentrel=0x7f4e66827980, 
    top_parentrc=0x0, lockmode=1, appinfos=0x28fce98) at prepunion.c:1713
1713        if (partdesc->nparts == 0)

expand_partitioned_rtentry->开始遍历分区描述符中的分区

1713        if (partdesc->nparts == 0)
(gdb) n
1719        for (i = 0; i < partdesc->nparts; i++)
(gdb) 
1721            Oid         childOID = partdesc->oids[i];
(gdb) 
1725            childrel = heap_open(childOID, NoLock);
(gdb) 
1732            if (RELATION_IS_OTHER_TEMP(childrel))
(gdb) 
1735            expand_single_inheritance_child(root, parentrte, parentRTindex,
(gdb) p childOID
$14 = 16989 
----------------------------------------
testdb=# select relname from pg_class where oid=16989;
      relname       
--------------------
 t_hash_partition_1
(1 row)
----------------------------------------

expand_single_inheritance_child->再次进入expand_single_inheritance_child

(gdb) step
expand_single_inheritance_child (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, parentrel=0x7f4e66827980, 
    top_parentrc=0x0, childrel=0x7f4e668306a0, appinfos=0x28fce98, childrte_p=0x7ffd1928d2f8, childRTindex_p=0x7ffd1928d2f4)
    at prepunion.c:1778
1778        Query      *parse = root->parse;

expand_single_inheritance_child->开始构建AppendRelInfo

...
1820        if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
(gdb) 
1822            appinfo = makeNode(AppendRelInfo);
(gdb) p *childrte
$17 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16989, relkind = 114 'r', tablesample = 0x0, subquery = 0x0, 
  security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, funcordinality = false, 
  tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, self_reference = false, coltypes = 0x0, 
  coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, enrtuples = 0, alias = 0x0, eref = 0x28fd9d0, lateral = false, 
  inh = false, inFromCl = true, requiredPerms = 0, checkAsUser = 0, selectedCols = 0x28fdbc8, insertedCols = 0x0, 
  updatedCols = 0x0, securityQuals = 0x0}
(gdb) p *childrte->relkind
Cannot access memory at address 0x72
(gdb) p childrte->relkind
$18 = 114 'r'
(gdb) p childrte->inh
$19 = false

expand_single_inheritance_child->构建完毕,查看AppendRelInfo结构体

(gdb) n
1823            appinfo->parent_relid = parentRTindex;
(gdb) 
1824            appinfo->child_relid = childRTindex;
(gdb) 
1825            appinfo->parent_reltype = parentrel->rd_rel->reltype;
(gdb) 
1826            appinfo->child_reltype = childrel->rd_rel->reltype;
(gdb) 
1827            make_inh_translation_list(parentrel, childrel, childRTindex,
(gdb) 
1829            appinfo->parent_reloid = parentOID;
(gdb) 
1830            *appinfos = lappend(*appinfos, appinfo);
(gdb) 
1841            if (childOID != parentOID)
(gdb) 
1843                childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
(gdb) 
1845                childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
(gdb) 
1847                childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
(gdb) 
1855        if (top_parentrc)
(gdb) p *appinfo
$20 = {type = T_AppendRelInfo, parent_relid = 1, child_relid = 3, parent_reltype = 16988, child_reltype = 16991, 
  translated_vars = 0x28fdc90, parent_reloid = 16986}

expand_single_inheritance_child->完成调用,返回

(gdb) 
1855        if (top_parentrc)
(gdb) p *appinfo
$20 = {type = T_AppendRelInfo, parent_relid = 1, child_relid = 3, parent_reltype = 16988, child_reltype = 16991, 
  translated_vars = 0x28fdc90, parent_reloid = 16986}
(gdb) n
1881    }
(gdb) 
expand_partitioned_rtentry (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, parentrel=0x7f4e66827980, 
    top_parentrc=0x0, lockmode=1, appinfos=0x28fce98) at prepunion.c:1740
1740            if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)

expand_inherited_rtentry->完成expand_partitioned_rtentry过程调用,回到expand_inherited_rtentry

(gdb) finish
Run till exit from #0  expand_partitioned_rtentry (root=0x28fcdc8, parentrte=0x28d83d0, parentRTindex=1, 
    parentrel=0x7f4e66827980, top_parentrc=0x0, lockmode=1, appinfos=0x28fce98) at prepunion.c:1740
0x00000000007e55e3 in expand_inherited_rtentry (root=0x28fcdc8, rte=0x28d83d0, rti=1) at prepunion.c:1603
1603            expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
(gdb) 

expand_inherited_rtentry->完成expand_inherited_rtentry的调用,回到expand_inherited_tables

(gdb) n
1665        heap_close(oldrelation, NoLock);
(gdb) 
1666    }
(gdb) 
expand_inherited_tables (root=0x28fcdc8) at prepunion.c:1490
1490            rl = lnext(rl);
(gdb) 

expand_inherited_tables->完成expand_inherited_tables调用,回到subquery_planner

(gdb) n
1485        for (rti = 1; rti <= nrtes; rti++)
(gdb) 
1492    }
(gdb) 
subquery_planner (glob=0x28fcd30, parse=0x28d82b8, parent_root=0x0, hasRecursion=false, tuple_fraction=0) at planner.c:719
719     root->hasHavingQual = (parse->havingQual != NULL);
(gdb) 

DONE!

四、参考资料

Parallel Append implementation
Partition Elimination in PostgreSQL 11

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PostgreSQL 源码解读(98)- 分区表#4(数据查询路由#1-“扩展”分区表)

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

下载Word文档

编程热搜

目录