2021SC@SDUSC
目录
概述
我负责的PostgreSQL代码部分:查询的编译与执行
此篇博客的分析内容:查询优化——生成计划
查询优化的整个过程可以分为预处理,生成路径和生成计划三个阶段。在上一篇博客中我分析了生成完整计划的过程和其中调用的函数在这篇博客中我会分析查询优化的最后一步同时也是生成计划的最后一步——整理计划树
生成的完整计划经过计划树整理后就可以交给查询执行器去执行了,负责整理工作的主函数是set_plan_references函数。整理计划树是查询优化器的最后一步,主要是为了方便执行器的执行,对计划树一些表达上的细节做最后的调整。例如,将上层的Var结构变为对子计划的输出结果的引用,获取操作符的OID等。同时,这一步也会删除那些没有任何用处的子查询扫描计划节点。在进行整理工作时会使用不同的函数来进行不同的整理动作,主要的整理函数如下:
| 主要函数 | 功能介绍 |
|---|---|
| fix_expr_references | 通过调用fix_expr_references_walker,完成表达式(目标属性表或条件表达式)的清理工作 |
| set_subqueryscan_references | 在SubqueryScan上进行set_plan_references的操作,即试图去除掉SubqueryScan节点 |
| fix_opfuncids | 通过调用fix_opfuncids_walker,在一个表达式的树中对操作符调用的表达式节点(OpExpr node)的值进行补全 |
| trivial_subqueryscan | 检测一个SubqueryScan是否可以从计划树中删除 |
| adjust_plan_varnos | 通过rtoffset调整varnos和其他所涉及的表的索引在计划树中的偏移量 |
| adjust_expr_varnos | 通过rtoffset来调整变量中的varnos在一个表达式中的偏移量 |
| fix_expr_references | 做表达式(如目标属性表或约束表达式)最后的清理工作 |
| set_join_references | 通过设置varnos为内连接或外连接和设置attno的值为内连接或外连接的结果的元组的数目来修改join节点的目标属性表和约束表达式 |
| set_inner_join_references | 处理出现在内部索引连接的表达式的join节点 |
| set_uppernode_references | 根据左子树子计划的返回元组来更新上层的目标变量列表和表达式 |
| build_tlist_index | 为一个子节点的目标属性表建立一个索引的结果 |
| search_indexed_tlist_for_var | 在一个索引的列表中寻找一个变量var,假如找到的话,返回它的一份拷贝,假如没有找到,则返回null |
| search_indexed_tlist_for_non_var | 在一个索引的列表中寻找一个非变量的节点,假如存在返回一个Var指向这个item,假如不存在,则返回null |
| join_references | 通过修改句子中的varno/varattno的值为外连接或内连接的目标属性表来建立一个join表达式或目标属性表的集合 |
| replace_vars_with_subplan_refs | 这个程序修改表达式树,使得所有的变量节点都与子计划的目标节点相关联。它主要用来处理上层计划节点中的非连接表达式或目标属性表 |
下面,我将挑选部分函数进行代码分析和函数讲解
set_plan_references函数
该函数的功能有
1:将各种子查询的范围表减少到一个单一的列表中,并且消除对执行者无用的RangeTblEntry字段
2:调整扫描节点中的变量,以匹配扁平的范围表
3:调整顶级计划节点中的变量,使它们指的是输出子计划
4:对ag计划节点中的汇总表局部聚合或最小聚合优化
5:PARAM_MULTIEXPR参数被普通的PARAM_EXEC参数所取代
6:计算运算符的regproc OIDs(即寻找实现每个运算符的函数)
7:创建计划所依赖的具体对象的清单,这些清单被plancache.c用来控制缓存计划的失效
8:为计划树上的每个计划节点分配一个唯一的ID
set_plan_references(PlannerInfo *root, Plan *plan)
{
PlannerGlobal *glob = root->glob;
int rtoffset = list_length(glob->finalrtable);
ListCell *lc;
//将所有查询的RTE添加到扁平化的范围表中。RTE将通过rtoffset增加它们的rangetable索引(额外的RTEs,没有被Plan树引用的,可能会在这些RTEs之后被添加)
add_rtes_to_flat_rtable(root, false);
//调整PlanRowMarks的RT索引并添加到最终的行标记列表中
foreach(lc, root->rowMarks)//遍历
{
PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
PlanRowMark *newrc;
//因为所有的字段都是标量,所以平面拷贝就足够了
newrc = (PlanRowMark *) palloc(sizeof(PlanRowMark));
memcpy(newrc, rc, sizeof(PlanRowMark));
//调整索引......但不是行标志的索引
newrc->rti += rtoffset;
newrc->prti += rtoffset;
//添加到finalrowmarks字段
glob->finalrowmarks = lappend(glob->finalrowmarks, newrc);
}
//修改计划树
return set_plan_refs(root, plan, rtoffset);
}
set_subqueryscan_references函数
在SubqueryScan上进行set_plan_references的操作,即试图去除掉SubqueryScan节点,如果不能,必须对其进行正常处理。
set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan,
int rtoffset)
{
RelOptInfo *rel;
Plan *result;
//需要查找子查询的RelOptInfo,因为需要它的子根
rel = find_base_rel(root, plan->scan.scanrelid);
//递归地处理子计划
plan->subplan = set_plan_references(rel

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



