这篇文章主要介绍“PostgreSQL中query_planner中主计划函数make_one_rel的主实现逻辑是什么”,在日常操作中,相信很多人在PostgreSQL中query_planner中主计划函数make_one_rel的主实现逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中query_planner中主计划函数make_one_rel的主实现逻辑是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
query_planner代码片段:
//...
final_rel = make_one_rel(root, joinlist);//执行主要的计划过程
if (!final_rel || !final_rel->cheapest_total_path ||
final_rel->cheapest_total_path->param_info != NULL)
elog(ERROR, "failed to construct the join relation");//检查
return final_rel;//返回结果
//...
一、数据结构
RelOptInfo
typedef struct RelOptInfo
{
NodeTag type;//节点标识
RelOptKind reloptkind;//RelOpt类型
Relids relids;
double rows;
bool consider_startup;
bool consider_param_startup;
bool consider_parallel;
struct PathTarget *reltarget;
List *pathlist;
List *ppilist;
List *partial_pathlist;
struct Path *cheapest_startup_path;//代价最低的启动路径
struct Path *cheapest_total_path;//代价最低的整体路径
struct Path *cheapest_unique_path;//代价最低的获取唯一值的路径
List *cheapest_parameterized_paths;//代价最低的参数化?路径链表
Relids direct_lateral_relids;
Relids lateral_relids;
//reloptkind=RELOPT_BASEREL时使用的数据结构
Index relid;
Oid reltablespace;
RTEKind rtekind;
AttrNumber min_attr;
AttrNumber max_attr;
Relids *attr_needed;
int32 *attr_widths;
List *lateral_vars;
Relids lateral_referencers;
List *indexlist;
List *statlist;
BlockNumber pages;
double tuples;
double allvisfrac;
PlannerInfo *subroot;
List *subplan_params;
int rel_parallel_workers;
//FWD相关信息
Oid serverid;
Oid userid;
bool useridiscurrent;
struct FdwRoutine *fdwroutine;
void *fdw_private;
//已知的,可保证唯一的Relids链表
List *unique_for_rels;
List *non_unique_for_rels;
List *baserestrictinfo;
QualCost baserestrictcost;
Index baserestrict_min_security;
List *joininfo;
bool has_eclass_joins;
bool consider_partitionwise_join;
Relids top_parent_relids;
//分区表使用
PartitionScheme part_scheme;
int nparts;
struct PartitionBoundInfoData *boundinfo;
List *partition_qual;
struct RelOptInfo **part_rels;
List **partexprs;
List **nullable_partexprs;
List *partitioned_child_rels;
} RelOptInfo;
二、源码解读
make_one_rel
make_one_rel函数找出执行查询的所有可能访问路径,但不考虑上层的Non-SPJ操作,返回一个最上层的RelOptInfo.
make_one_rel函数分为两个阶段:生成扫描路径(set_base_rel_pathlists)和生成连接路径(make_rel_from_joinlist).
注:SPJ是指选择(Select)/投影(Project)/连接(Join),相对应的Non-SPJ操作是指Group分组/Sort排序等操作
RelOptInfo *
make_one_rel(PlannerInfo *root, List *joinlist)
{
RelOptInfo *rel;
Index rti;
root->all_baserels = NULL;
for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo
{
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;
Assert(brel->relid == rti);
if (brel->reloptkind != RELOPT_BASEREL)
continue;
root->all_baserels = bms_add_member(root->all_baserels, brel->relid);//添加到all_baserels遍历中
}
//设置RelOptInfo的consider_param_startup变量,是否考量fast-start plans
set_base_rel_consider_startup(root);
set_base_rel_sizes(root);//估算Relation的Size并且设置consider_parallel标记
set_base_rel_pathlists(root);//生成Relation的扫描(访问)路径
rel = make_rel_from_joinlist(root, joinlist);
Assert(bms_equal(rel->relids, root->all_baserels));
//返回最上层的RelOptInfo
return rel;
}
//--------------------------------------------------------
static void
set_base_rel_consider_startup(PlannerInfo *root)
{
ListCell *lc;
foreach(lc, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
int varno;
if ((sjinfo->jointype == JOIN_SEMI || sjinfo->jointype == JOIN_ANTI) &&
bms_get_singleton_member(sjinfo->syn_righthand, &varno))
{
RelOptInfo *rel = find_base_rel(root, varno);
rel->consider_param_startup = true;
}
}
}
//--------------------------------------------------------
static void
set_base_rel_sizes(PlannerInfo *root)
{
Index rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo数组
{
RelOptInfo *rel = root->simple_rel_array[rti];
RangeTblEntry *rte;
if (rel == NULL)
continue;
Assert(rel->relid == rti);
if (rel->reloptkind != RELOPT_BASEREL)
continue;
rte = root->simple_rte_array[rti];
if (root->glob->parallelModeOK)
set_rel_consider_parallel(root, rel, rte);
set_rel_size(root, rel, rti, rte);
}
}
static void
set_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
if (rel->reloptkind == RELOPT_BASEREL &&
relation_excluded_by_constraints(root, rel, rte))
{
set_dummy_rel_pathlist(rel);//
}
else if (rte->inh)//inherit table
{
set_append_rel_size(root, rel, rti, rte);
}
else
{
switch (rel->rtekind)
{
case RTE_RELATION://数据表
if (rte->relkind == RELKIND_FOREIGN_TABLE)//FDW
{
set_foreign_size(root, rel, rte);
}
else if (rte->relkind == RELKIND_PARTITIONED_TABLE)//分区表
{
set_dummy_rel_pathlist(rel);
}
else if (rte->tablesample != NULL)//采样表
{
set_tablesample_rel_size(root, rel, rte);
}
else
{
set_plain_rel_size(root, rel, rte);//常规的数据表
}
break;
case RTE_SUBQUERY://子查询
set_subquery_pathlist(root, rel, rti, rte);//生成子查询访问路径
break;
case RTE_FUNCTION://FUNCTION
set_function_size_estimates(root, rel);
break;
case RTE_TABLEFUNC://TABLEFUNC
set_tablefunc_size_estimates(root, rel);
break;
case RTE_VALUES://VALUES
set_values_size_estimates(root, rel);
break;
case RTE_CTE://CTE
if (rte->self_reference)
set_worktable_pathlist(root, rel, rte);
else
set_cte_pathlist(root, rel, rte);
break;
case RTE_NAMEDTUPLESTORE://NAMEDTUPLESTORE,命名元组存储
set_namedtuplestore_pathlist(root, rel, rte);
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
}
}
Assert(rel->rows > 0 || IS_DUMMY_REL(rel));
}
//--------------------------------------------------------
static void
set_base_rel_pathlists(PlannerInfo *root)
{
Index rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo数组
{
RelOptInfo *rel = root->simple_rel_array[rti];
if (rel == NULL)
continue;
Assert(rel->relid == rti);
if (rel->reloptkind != RELOPT_BASEREL)
continue;
set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti]);
}
}
static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
if (IS_DUMMY_REL(rel))
{
}
else if (rte->inh)//inherit
{
set_append_rel_pathlist(root, rel, rti, rte);
}
else//常规
{
switch (rel->rtekind)
{
case RTE_RELATION://数据表
if (rte->relkind == RELKIND_FOREIGN_TABLE)//FDW
{
set_foreign_pathlist(root, rel, rte);
}
else if (rte->tablesample != NULL)//采样表
{
set_tablesample_rel_pathlist(root, rel, rte);
}
else//常规数据表
{
set_plain_rel_pathlist(root, rel, rte);
}
break;
case RTE_SUBQUERY://子查询
break;
case RTE_FUNCTION:
set_function_pathlist(root, rel, rte);
break;
case RTE_TABLEFUNC:
set_tablefunc_pathlist(root, rel, rte);
break;
case RTE_VALUES:
set_values_pathlist(root, rel, rte);
break;
case RTE_CTE:
break;
case RTE_NAMEDTUPLESTORE:
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
}
}
if (rel->reloptkind == RELOPT_BASEREL &&
bms_membership(root->all_baserels) != BMS_SINGLETON)
generate_gather_paths(root, rel, false);
if (set_rel_pathlist_hook)//钩子函数
(*set_rel_pathlist_hook) (root, rel, rti, rte);
set_cheapest(rel);//找出代价最低的访问路径
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
#endif
}
//------------------------------------------------------------
static RelOptInfo *
make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
{
int levels_needed;
List *initial_rels;
ListCell *jl;
levels_needed = list_length(joinlist);//joinlist链表长度
if (levels_needed <= 0)
return NULL;
initial_rels = NIL;
foreach(jl, joinlist)//遍历链表
{
Node *jlnode = (Node *) lfirst(jl);
RelOptInfo *thisrel;
if (IsA(jlnode, RangeTblRef))//RTR
{
int varno = ((RangeTblRef *) jlnode)->rtindex;
thisrel = find_base_rel(root, varno);
}
else if (IsA(jlnode, List))
{
thisrel = make_rel_from_joinlist(root, (List *) jlnode);//递归调用
}
else
{
elog(ERROR, "unrecognized joinlist node type: %d",
(int) nodeTag(jlnode));
thisrel = NULL;
}
initial_rels = lappend(initial_rels, thisrel);//加入初始化链表中
}
if (levels_needed == 1)
{
return (RelOptInfo *) linitial(initial_rels);
}
else
{
root->initial_rels = initial_rels;
if (join_search_hook)//钩子函数
return (*join_search_hook) (root, levels_needed, initial_rels);
else if (enable_geqo && levels_needed >= geqo_threshold)
return geqo(root, levels_needed, initial_rels);//通过遗传算法构建连接访问路径
else
return standard_join_search(root, levels_needed, initial_rels);//通过动态规划算法构建连接路径
}
}
到此,关于“PostgreSQL中query_planner中主计划函数make_one_rel的主实现逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!