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文档到电脑,方便收藏和打印~