openGauss源码解析(98)

openGauss源码解析:SQL引擎源解析(13)

2. 路径生成

前面已经介绍了路径生成中使用的动态规划方法,并且介绍了在累积过程中如何生成当前层的RelOptInfo。对于生成当前层的RelOptInfo会面临几个问题:一是需要判断两个RelOptInfo是否可以进行连接,二是生成物理连接路径。目前物理连接路径主要有三种实现,分别是NestLoopJoin、HashJoin和MergeJoin,建立连接路径的过程就是不断地尝试生成这三种路径的过程。

1) 检查

在动态规划方法中需要将N-1层的每个RelOptInfo和第1层的每个RelOptInfo尝试做连接,然后将新连接的RelOptInfo保存在当前第N层,算法的时间复杂度在O(M×N)左右,如果发生第N-1层和第1层中RelOptInfo都比较多的情况下,搜索空间会膨胀得比较大。但有些RelOptInfo在做连接的时候是可以避免掉的,这也是我们需要及时检查的目的,提前检测出并且跳过两个RelOptInfo之间的链接,会节省不必要的开销,提升优化器生成优化的效率。

(1) 初步检查。

下面几个条件是初步检查主要进行衡量的因素。

一是RelOptinfo中joininfo不为NULL。那就说明这个RelOptInfo和其他的RelOptInfo存在相关的约束条件,换言之就是说当前这个RelOptInfo可能和其他表存在关联。
二是RelOptInfo中has_eclass_joins为true,表明在等价类的记录中当前RelOptInfo和其他RelOptInfo可能存在等值连接条件。
三是has_join_restriction函数的返回值为true,说明当前的RelOptInfo和其他的RelOptInfo有连接顺序的限制。

初步检查就是利用RelOptInfo的信息进行一种“可能性”的判断,主要是检测是否有连接条件和连接顺序的约束。

static bool has_join_restriction(PlannerInfo* root, RelOptInfo* rel)

{

ListCell* l = NULL;

//如果当前RelOptInfo涉及Lateral语义,那么就一定有连接顺序约束

foreach(l, root->lateral_info_list)

{

LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);

if (bms_is_member(ljinfo->lateral_rhs, rel->relids) ||

bms_overlap(ljinfo->lateral_lhs, rel->relids))

return true;

}

//仅处理除内连接之外的条件

foreach (l, root->join_info_list) {

SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l);

//跳过全连接检查,会有其他机制保证其连接顺序

if (sjinfo->jointype == JOIN_FULL)

continue;

//如果这个SpecialJoinInfo已经被RelOptInfo包含就跳过

if (bms_is_subset(sjinfo->min_lefthand, rel->relids) &&

bms_is_subset(sjinfo->min_righthand, rel->relids))

continue;

//如果RelOptInfo结构体的relids变量和min_lefthand变量或min_righthand变量有交集,那么它就有可能有连接顺序的限制

if (bms_overlap(sjinfo->min_lefthand, rel->relids) ||

bms_overlap(sjinfo->min_righthand, rel->relids))

return true;

}

return false;

}

(1) 精确检查。

在进行了初步检查之后,如果判断出两侧RelOptInfo不存在有连接条件或者连接顺序的限制,那么就进入make_rels_by_clauseless_joins函数中,将RelOptInfo中所有可能的路径和第1层RelOptInfo进行连接。如果当前RelOptInfo可能有连接条件或者连接顺序的限制,那么就会进入make_rel_by_clause_joins函数中,会逐步将当前的RelOptInfo和第1层其他RelOptInfo进一步检查以确定是否可以进行连接。

在have_join_order_restriction函数判断两个RelOptInfo是否具有连接顺序的限制,主要从两个方面判断是否具有连接顺序的限制:一是判断两个RelOptInfo之间是否具有Lateral语义的顺序的限制,二是判断SpecialJoinInfo中的min_lefthand和min_righthand是否对两个RelOptInfo具有连接顺序的限制。

对have_join_order_restriction部分源码分析如下:

bool have_join_order_restriction(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo* rel2)

{

bool result = false;

ListCell* l = NULL;

//如果有Lateral语义的依赖关系,则一定具有连接顺序的限制

foreach(l, root->lateral_info_list)

{

LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);

if (bms_is_member(ljinfo->lateral_rhs, rel2->relids) &&

bms_overlap(ljinfo->lateral_lhs, rel1->relids))

return true;

if (bms_is_member(ljinfo->lateral_rhs, rel1->relids) &&

bms_overlap(ljinfo->lateral_lhs, rel2->relids))

return true;

}

//遍历root目录中所有SpecialJoinInfo,判断两个RelOptInfo是否具有连接限制

foreach (l, root->join_info_list) {

SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l);

if (sjinfo->jointype == JOIN_FULL)

continue;

//"最小集"分别是两个表的子集,两个表需要符合连接顺序

if (bms_is_subset(sjinfo->min_lefthand, rel1->relids) &&

bms_is_subset(sjinfo->min_righthand, rel2->relids)) {

result = true;

break;

}

//反过来同上,"最小集"分别是两个表的子集,两个表需要符合连接顺序

if (bms_is_subset(sjinfo->min_lefthand, rel2->relids) &&

bms_is_subset(sjinfo->min_righthand, rel1->relids)) {

result = true;

break;

}

//如果两个表都和最小集的一端有交集,那么这两个表应该在这一端下做连接

//故让他们先做连接

if (bms_overlap(sjinfo->min_righthand, rel1->relids) && bms_overlap(sjinfo->min_righthand, rel2->relids)) {

result = true;

break;

}

//反过来同上

if (bms_overlap(sjinfo->min_lefthand, rel1->relids) && bms_overlap(sjinfo->min_lefthand, rel2->relids)) {

result = true;

break;

}

}

//如果两个表上和其他表有相对应的连接关系

//那么可以让他们先和具有连接关系的表进行连接

if (result) {

if (has_legal_joinclause(root, rel1) || has_legal_joinclause(root, rel2))

result = false;

}

return result;

}

posted @ 2024-04-30 10:35  openGauss-bot  阅读(11)  评论(0)    收藏  举报