查询规划的主要工作由grouping_planner函数完成。在具体实现的时候,针对postgresql中独有的继承表,程序使用inheritance_planner函数来解决,该函数主要是先将继承表的继承关系变换为非继承表来处理,然后仍然调用的是grouping_planner函数来完成查询规划的工作。
3.查询规划处理
这里大家真的要做好准备,因为grouping_planner函数本身就有将近1000行~
那什么,我们还是先上图吧。有图更清楚,文字太多大家也会晕的。以下是grouping_planner函数的流程图。

grouping_planner函数是生成查询计划树的主要函数。该函数首先要考虑查询计划中是否有集合操作(可通过查询树的setOperation变量来判断)。如果有则需要进行集合操作:遍历setOperation,为其中的每一个子查询生成计划。而对于非集合操作,计划的生成过程如下:
-
如果查询含有GROUP BY子句,那么就调整其GROUP BY属性的顺序以匹配ORDER BY子句中的属性顺序(如果使用了grouping sets,则需要做进一步的变换,对于grouping sets,可以看这篇文章),这样就可以使用一次排序操作同时实现排序和分组;
-
调用preprocess_targetlist预处理INSERT、UPDATE、DELETE和FOR UPDATE情况下的目标属性;
-
计算并确定代表排序需求的路径关键字,按优先级递减排序主要有groupClause、WindowClause,distinctClause和sortClause;
-
调用query_planner函数为一个基本查询创建路径并获得cheapest_path(代价最低执行路径),再调用get_cheapest_fractional_path_for_pathkeys函数获得sorted_path(对排序最优的路径);
-
依据是否存在Hash等条件,确定最优路径best_path;
-
生成可优化的MIN/MAX聚集计划;
-
如果6)中没有生成聚集计划,则调用create_plan函数生成普通计划;
-
根据是否有GROUP BY子句、聚集操作、ORDER BY子句、DISTINCT子句和LIMIT子句等添加相应的计划节点.
3.1 生成路径
SQL语句说白了就是从数据库中获取元组,之后再进行增删改查的操作。因此对于一个查询计划来说,重要的是告诉查询执行模块如何获取到要操作的元组。而这些元组要么来自于某张表,要么来自于一些基本表连接而成的"连接表"。对于这些连接表来说,会存在多种不同的连接方式,从而形成多种连接树(逻辑结构)。这样的每一棵树在postgresql中都成为一条路径。查询规划的目的莫过于从这些路径中选取一条最优的路径并生成对应的查询计划。
而生成路径的工作就是query_planner函数来完成的。不废话,上图。

在query_planner函数内部,首先我们要判断一下由PlannerInfo封装的查询树的jointree是否存在。为什么要做这样的判断呢?因为那些没有jointree的查询可能是"SELECT 2+2;"或者"INSERT ... VALUES()"这样的形式。对于他们的处理和对于有jointree的处理是不一样的。
对于没有jointree的查询树,为了和有jointree的查询树在返回的路径形式上保持一致,我们调用build_empty_join_rel函数来为它们构建一个空的join。这样以后我们就直接调用create_result_path函数和add_path函数为其创建了一条"冗余"(因为这条路径本来就不存在,数据并不在数据库里面)的路径。值得注意的是,我们仍然要为这样的路径做排序操作,因为我们可能会写出这样的查询语句:"SELECT 2+2 ORDER BY 1" 。虽然是毫无意义的排序,但是我们还是要处理的不是么。至此,返回查询路径,函数退出。
而对于有jointree的查询树,情况一定是更复杂了。首先要为PlannerInfo里面各种参数以及各种JOIN(left_join,right_join,fu

本文详细介绍PostgreSQL中的查询规划过程,包括grouping_planner函数的工作原理、路径生成算法、查询计划生成及计划树整理等内容。
最低0.47元/天 解锁文章
7820

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



