PostgreSQL——查询优化——整理计划树

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_47373497

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值