enter
This commit is contained in:
parent
c4a23cca76
commit
332fbf113f
|
|
@ -34,12 +34,19 @@
|
|||
#include "utils/lsyscache.h"
|
||||
|
||||
/* local functions */
|
||||
// 检查特殊连接是否可移除
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo);
|
||||
// 从查询中移除关系
|
||||
static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids);
|
||||
// 从连接列表中移除关系
|
||||
static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved);
|
||||
// 检查关系是否支持去重
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel);
|
||||
// 检查关系是否对指定子句去重
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list);
|
||||
// 检查列的唯一性
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids);
|
||||
// 查找具有唯一性的列
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids);
|
||||
|
||||
/*
|
||||
|
|
@ -50,23 +57,25 @@ static Oid distinct_col_search(int colno, List* colnos, List* opids);
|
|||
* We are passed the current joinlist and return the updated list. Other
|
||||
* data structures that have to be updated are accessible via "root".
|
||||
*/
|
||||
List* remove_useless_joins(PlannerInfo* root, List* joinlist)
|
||||
List* remove_useless_joins(PlannerInfo* root, List* joinlist)// 移除无用的连接
|
||||
{
|
||||
ListCell* lc = NULL;
|
||||
ListCell* pnext = NULL;
|
||||
ListCell* lc = NULL;// 用于遍历join_info_list的指针
|
||||
ListCell* pnext = NULL;// 用于保存下一个元素的指针
|
||||
|
||||
/*
|
||||
* We are only interested in relations that are left-joined to, so we can
|
||||
* scan the join_info_list to find them easily.
|
||||
*/
|
||||
restart:
|
||||
// 遍历特殊连接信息列表 join_info_list
|
||||
for (lc = list_head(root->join_info_list); lc != NULL; lc = pnext) {
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc);
|
||||
pnext = lnext(lc);
|
||||
int innerrelid;
|
||||
int nremoved;
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc);// 获取特殊连接信息
|
||||
pnext = lnext(lc); // 保存下一个元素的指针
|
||||
int innerrelid;// 内部关系的标识符
|
||||
int nremoved;// 移除的关系数量
|
||||
|
||||
/* Skip if not removable */
|
||||
// 检查特殊连接是否可移除,如果不可移除则继续下一个连接
|
||||
if (!join_is_removable(root, sjinfo))
|
||||
continue;
|
||||
|
||||
|
|
@ -75,14 +84,14 @@ restart:
|
|||
* righthand is a single baserel. Remove that rel from the query and
|
||||
* joinlist.
|
||||
*/
|
||||
innerrelid = bms_singleton_member(sjinfo->min_righthand);
|
||||
innerrelid = bms_singleton_member(sjinfo->min_righthand);// 获取内部关系的标识符
|
||||
|
||||
remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand));
|
||||
remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand));// 从查询中移除内部关系
|
||||
|
||||
/* We verify that exactly one reference gets removed from joinlist */
|
||||
nremoved = 0;
|
||||
joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);
|
||||
if (nremoved != 1)
|
||||
joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
if (nremoved != 1)// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
|
|
@ -92,7 +101,7 @@ restart:
|
|||
* We can delete this SpecialJoinInfo from the list too, since it's no
|
||||
* longer of interest.
|
||||
*/
|
||||
root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
|
||||
root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
|
||||
/*
|
||||
* Restart the scan. This is necessary to ensure we find all
|
||||
|
|
@ -102,10 +111,10 @@ restart:
|
|||
* current list cell, we'd have to have some kluge to continue the
|
||||
* list scan anyway.
|
||||
*/
|
||||
goto restart;
|
||||
goto restart;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
}
|
||||
|
||||
return joinlist;
|
||||
return joinlist;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -118,18 +127,21 @@ restart:
|
|||
* rather than mixing outer and inner vars on either side. If it matches,
|
||||
* we set the transient flag outer_is_left to identify which side is which.
|
||||
*/
|
||||
static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids)
|
||||
static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids)// 内联函数,检查子句的两侧是否匹配连接
|
||||
|
||||
{
|
||||
// 检查约束信息的左侧关系是否是外部关系的子集,右侧关系是否是内部关系的子集
|
||||
if (bms_is_subset(rinfo->left_relids, outerrelids) && bms_is_subset(rinfo->right_relids, innerrelids)) {
|
||||
/* lefthand side is outer */
|
||||
rinfo->outer_is_left = true;
|
||||
return true;
|
||||
rinfo->outer_is_left = true;// 设置 outer_is_left 为 true,表示左侧关系在连接中是外部关系
|
||||
return true;// 返回 true,表示约束信息的关系符合连接
|
||||
} else if (bms_is_subset(rinfo->left_relids, innerrelids) && bms_is_subset(rinfo->right_relids, outerrelids)) {
|
||||
/* righthand side is outer */
|
||||
rinfo->outer_is_left = false;
|
||||
return true;
|
||||
rinfo->outer_is_left = false;// 设置 outer_is_left 为 false,表示左侧关系在连接中是内部关系
|
||||
return true;// 返回 true,表示约束信息的关系符合连接
|
||||
}
|
||||
return false; /* no good for these input relations */
|
||||
// 如果左右关系都不符合连接,返回 false
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -143,7 +155,7 @@ static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerreli
|
|||
* have to check that the inner side doesn't generate any variables needed
|
||||
* above the join.
|
||||
*/
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检查连接是否可移除
|
||||
{
|
||||
int innerrelid;
|
||||
RelOptInfo* innerrel = NULL;
|
||||
|
|
@ -156,6 +168,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* Must be a non-delaying left join to a single baserel, else we aren't
|
||||
* going to be able to do anything with it.
|
||||
*/
|
||||
// 仅适用于左连接和左反连接,且右侧关系为单一关系
|
||||
if ((sjinfo->jointype != JOIN_LEFT && sjinfo->jointype != JOIN_LEFT_ANTI_FULL) || sjinfo->delay_upper_joins ||
|
||||
bms_membership(sjinfo->min_righthand) != BMS_SINGLETON)
|
||||
return false;
|
||||
|
|
@ -168,11 +181,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* are needed above the join, make a quick check to eliminate cases in
|
||||
* which we will surely be unable to prove uniqueness of the innerrel.
|
||||
*/
|
||||
if (!rel_supports_distinctness(root, innerrel))
|
||||
if (!rel_supports_distinctness(root, innerrel))// 检查右侧关系是否支持去重
|
||||
return false;
|
||||
|
||||
/* Compute the relid set for the join we are considering */
|
||||
joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
|
||||
joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);// 计算连接关系的集合
|
||||
|
||||
/*
|
||||
* We can't remove the join if any inner-rel attributes are used above the
|
||||
|
|
@ -187,7 +200,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* theory that the system attributes are somewhat less likely to be wanted
|
||||
* and should be tested last.
|
||||
*/
|
||||
for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) {
|
||||
for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) {// 检查左侧关系所需的属性是否完全包含在右侧关系中
|
||||
if (!bms_is_subset(innerrel->attr_needed[attroff], joinrelids))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -199,7 +212,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* for that is relatively expensive, so we first check against ph_eval_at,
|
||||
* which must mention the inner rel if the PHV uses any inner-rel attrs.
|
||||
*/
|
||||
foreach (l, root->placeholder_list) {
|
||||
foreach (l, root->placeholder_list) {// 检查占位符的需要属性是否完全包含在右侧关系中
|
||||
PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l);
|
||||
|
||||
if (bms_is_subset(phinfo->ph_needed, joinrelids))
|
||||
|
|
@ -217,7 +230,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* it's what we want. The mergejoinability test also eliminates clauses
|
||||
* containing volatile functions, which we couldn't depend on.
|
||||
*/
|
||||
foreach (l, innerrel->joininfo) {
|
||||
foreach (l, innerrel->joininfo) {// 遍历右侧关系的连接信息,检查是否符合移除条件
|
||||
RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(l);
|
||||
|
||||
/*
|
||||
|
|
@ -226,6 +239,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* above the outer join, even if it references no other rels (it might
|
||||
* be from WHERE, for example).
|
||||
*/
|
||||
// 如果约束已下推或所需关系不符合连接关系,则跳过
|
||||
if (restrictinfo->is_pushed_down || !bms_equal(restrictinfo->required_relids, joinrelids)) {
|
||||
/*
|
||||
* If such a clause actually references the inner rel then join
|
||||
|
|
@ -246,11 +260,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* Check if clause has the form "outer op inner" or "inner op outer",
|
||||
* and if so mark which side is inner.
|
||||
*/
|
||||
if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids))
|
||||
if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids))// 检查约束的两侧是否匹配连接
|
||||
continue; /* no good for these input relations */
|
||||
|
||||
/* OK, add to list */
|
||||
clause_list = lappend(clause_list, restrictinfo);
|
||||
clause_list = lappend(clause_list, restrictinfo); // 将符合条件的约束加入列表
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -277,38 +291,42 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
|||
* Also, join quals involving the rel have to be removed from the joininfo
|
||||
* lists, but only if they belong to the outer join identified by joinrelids.
|
||||
*/
|
||||
// 静态函数,用于从查询计划中移除与给定 relid 关联的关系
|
||||
static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids)
|
||||
{
|
||||
RelOptInfo* rel = find_base_rel(root, relid);
|
||||
List* joininfos = NIL;
|
||||
Index rti;
|
||||
ListCell* l = NULL;
|
||||
RelOptInfo* rel = find_base_rel(root, relid);// 查找与给定 relid 相关的 RelOptInfo 结构体
|
||||
List* joininfos = NIL;// 用于存储连接信息的列表
|
||||
Index rti;// 用于遍历 RelOptInfo 结构体数组的索引
|
||||
ListCell* l = NULL;// 用于循环迭代的列表元素指针
|
||||
ListCell* nextl = NULL;
|
||||
|
||||
/*
|
||||
* Mark the rel as "dead" to show it is no longer part of the join tree.
|
||||
* (Removing it from the baserel array altogether seems too risky.)
|
||||
*/
|
||||
rel->reloptkind = RELOPT_DEADREL;
|
||||
rel->reloptkind = RELOPT_DEADREL;// 将 rel 标记为已删除状态
|
||||
|
||||
/*
|
||||
* Remove references to the rel from other baserels' attr_needed arrays.
|
||||
*/
|
||||
for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) {
|
||||
RelOptInfo* otherrel = root->simple_rel_array[rti];
|
||||
for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) {// 遍历 simple_rel_array 数组中的 RelOptInfo 结构体
|
||||
RelOptInfo* otherrel = root->simple_rel_array[rti]; // 获取当前的 RelOptInfo 结构体
|
||||
int attroff;
|
||||
|
||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||
|
||||
// 如果 RelOptInfo 结构体为空,则继续下一轮循环
|
||||
if (otherrel == NULL)
|
||||
continue;
|
||||
|
||||
// 断言确保 RelOptInfo 的索引正确
|
||||
AssertEreport(otherrel->relid == rti, MOD_OPT, "RelOptInfo Index Incorrect.");
|
||||
|
||||
/* no point in processing target rel itself */
|
||||
if (otherrel == rel)
|
||||
if (otherrel == rel)// 如果当前的 RelOptInfo 与目标 rel 相同,则继续下一轮循环
|
||||
continue;
|
||||
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) {
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) {// 遍历属性列表,从属性的 attr_needed 中删除 relid
|
||||
otherrel->attr_needed[attroff] = bms_del_member(otherrel->attr_needed[attroff], relid);
|
||||
}
|
||||
}
|
||||
|
|
@ -321,7 +339,7 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* RHS of the target outer join will be made empty here, but that's OK
|
||||
* since caller will delete that SpecialJoinInfo entirely.
|
||||
*/
|
||||
foreach (l, root->join_info_list) {
|
||||
foreach (l, root->join_info_list) {// 遍历 join_info_list 列表,更新连接信息中的左右关系
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l);
|
||||
|
||||
sjinfo->min_lefthand = bms_del_member(sjinfo->min_lefthand, relid);
|
||||
|
|
@ -338,11 +356,12 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* included in any lateral_lhs set. (It probably can't be, since that
|
||||
* should have precluded deciding to remove it; but let's cope anyway.)
|
||||
*/
|
||||
for (l = list_head(root->lateral_info_list); l != NULL; l = nextl)
|
||||
for (l = list_head(root->lateral_info_list); l != NULL; l = nextl)// 遍历 lateral_info_list 列表,更新 lateral 关系信息
|
||||
{
|
||||
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
|
||||
|
||||
nextl = lnext(l);
|
||||
// 如果 lateral_rhs 与给定的 relid 相同,则从列表中删除该项
|
||||
if (ljinfo->lateral_rhs == (Index)relid)
|
||||
root->lateral_info_list = list_delete_ptr(root->lateral_info_list,
|
||||
ljinfo);
|
||||
|
|
@ -358,14 +377,14 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* set. An empty eval_at set would confuse later processing since it
|
||||
* would match every possible eval placement.
|
||||
*/
|
||||
foreach (l, root->placeholder_list) {
|
||||
foreach (l, root->placeholder_list) {// 遍历 placeholder_list 列表,更新占位符信息
|
||||
PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l);
|
||||
|
||||
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);
|
||||
if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */
|
||||
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);// 从 ph_eval_at 中删除 relid
|
||||
if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */// 如果 ph_eval_at 变为空集,将 relid 添加回去
|
||||
phinfo->ph_eval_at = bms_add_member(phinfo->ph_eval_at, relid);
|
||||
|
||||
phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);
|
||||
phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);// 从 ph_needed 中删除 relid
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -382,22 +401,22 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* loop, because otherwise remove_join_clause_from_rels would destroy the
|
||||
* list while we're scanning it.
|
||||
*/
|
||||
joininfos = list_copy(rel->joininfo);
|
||||
joininfos = list_copy(rel->joininfo);// 复制 rel 的 joininfo 列表,并遍历处理
|
||||
foreach (l, joininfos) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(l);
|
||||
|
||||
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
|
||||
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);// 从连接信息中移除与 relid 相关的子句
|
||||
|
||||
if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) {
|
||||
if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) {// 如果子句已被推送下来或者不等于 joinrelids,则进行处理
|
||||
/* Recheck that qual doesn't actually reference the target rel */
|
||||
AssertEreport(!bms_is_member(relid, rinfo->clause_relids), MOD_OPT, "");
|
||||
/*
|
||||
* The required_relids probably aren't shared with anything else,
|
||||
* but let's copy them just to be sure.
|
||||
*/
|
||||
rinfo->required_relids = bms_copy(rinfo->required_relids);
|
||||
rinfo->required_relids = bms_copy(rinfo->required_relids);// 复制 required_relids,然后从中删除 relid
|
||||
rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
|
||||
distribute_restrictinfo_to_rels(root, rinfo);
|
||||
distribute_restrictinfo_to_rels(root, rinfo);// 将处理后的信息重新分发到相关的 RelOptInfo 结构体中
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,22 +430,23 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* *nremoved is incremented by the number of occurrences removed (there
|
||||
* should be exactly one, but the caller checks that).
|
||||
*/
|
||||
// 静态函数,从连接列表中移除与给定 relid 相关的关系
|
||||
static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
||||
{
|
||||
List* result = NIL;
|
||||
ListCell* jl = NULL;
|
||||
List* result = NIL;// 用于存储结果的列表
|
||||
ListCell* jl = NULL;// 用于遍历连接列表的列表元素指针
|
||||
|
||||
foreach (jl, joinlist) {
|
||||
Node* jlnode = (Node*)lfirst(jl);
|
||||
foreach (jl, joinlist) {// 遍历连接列表
|
||||
Node* jlnode = (Node*)lfirst(jl);// 获取当前列表元素
|
||||
|
||||
if (IsA(jlnode, RangeTblRef)) {
|
||||
if (IsA(jlnode, RangeTblRef)) {// 如果当前元素是 RangeTblRef
|
||||
int varno = ((RangeTblRef*)jlnode)->rtindex;
|
||||
|
||||
if (varno == relid)
|
||||
if (varno == relid)// 如果 varno 等于 relid,则增加 nremoved 计数
|
||||
(*nremoved)++;
|
||||
else
|
||||
else// 否则将当前元素添加到结果列表中
|
||||
result = lappend(result, jlnode);
|
||||
} else if (IsA(jlnode, List)) {
|
||||
} else if (IsA(jlnode, List)) {// 如果当前元素是 List,则递归调用 remove_rel_from_joinlist 处理子列表
|
||||
/* Recurse to handle subproblem */
|
||||
List* sublist = NIL;
|
||||
|
||||
|
|
@ -434,7 +454,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
/* Avoid including empty sub-lists in the result */
|
||||
if (sublist != NIL)
|
||||
result = lappend(result, sublist);
|
||||
} else {
|
||||
} else {// 如果是其他类型的节点,则报错
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_UNEXPECTED_NODE_STATE),
|
||||
|
|
@ -443,7 +463,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;// 返回处理后的结果列表
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -457,7 +477,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
* rel_is_distinct_for()'s argument lists if the call could not possibly
|
||||
* succeed.
|
||||
*/
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静态函数,判断 rel 是否支持 DISTINCT 操作
|
||||
{
|
||||
/*
|
||||
* We only handle two cases here:
|
||||
|
|
@ -465,9 +485,9 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)
|
|||
* 2. subquery with aggreations
|
||||
* We can improve later when we can propagate uniqueness
|
||||
*/
|
||||
if (rel->reloptkind != RELOPT_BASEREL)
|
||||
if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT
|
||||
return false;
|
||||
if (rel->rtekind == RTE_RELATION) {
|
||||
if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION)
|
||||
/*
|
||||
* For a plain relation, we only know how to prove uniqueness by
|
||||
* reference to unique indexes. Make sure there's at least one
|
||||
|
|
@ -475,17 +495,18 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)
|
|||
* it's a partial index, it must match the query. (Keep these
|
||||
* conditions in sync with relation_has_unique_index_for!)
|
||||
*/
|
||||
// 遍历索引列表,查找唯一且立即可用的索引
|
||||
ListCell* lc = NULL;
|
||||
|
||||
foreach (lc, rel->indexlist) {
|
||||
IndexOptInfo* ind = (IndexOptInfo*)lfirst(lc);
|
||||
|
||||
// 如果索引是唯一的、立即可用的,并且没有过滤条件,则支持 DISTINCT
|
||||
if (ind->unique && ind->immediate && (ind->indpred == NIL || ind->predOK))
|
||||
return true;
|
||||
}
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {
|
||||
Query* subquery = root->simple_rte_array[rel->relid]->subquery;
|
||||
|
||||
// 如果 rel 是子查询,则判断子查询是否支持 DISTINCT
|
||||
/* Check if the subquery has any qualities that support distinctness */
|
||||
if (query_supports_distinctness(subquery))
|
||||
return true;
|
||||
|
|
@ -512,24 +533,25 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)
|
|||
* is OK for current uses, because the clause_list is built by the caller for
|
||||
* the sole purpose of passing to this function.
|
||||
*/
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list)
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list)// 静态函数,判断 rel 是否对给定的列列表支持 DISTINCT 操作
|
||||
{
|
||||
/*
|
||||
* We could skip a couple of tests here if we assume all callers checked
|
||||
* rel_supports_distinctness first, but it doesn't seem worth taking any
|
||||
* risk for.
|
||||
*/
|
||||
if (rel->reloptkind != RELOPT_BASEREL)
|
||||
if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT
|
||||
return false;
|
||||
if (rel->rtekind == RTE_RELATION) {
|
||||
if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION)
|
||||
|
||||
/*
|
||||
* Examine the indexes to see if we have a matching unique index.
|
||||
* relation_has_unique_index_for automatically adds any usable
|
||||
* restriction clauses for the rel, so we needn't do that here.
|
||||
*/
|
||||
if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL))
|
||||
if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL))// 调用 relation_has_unique_index_for 函数判断是否有唯一索引支持 DISTINCT
|
||||
return true;
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {// 如果 rel 是子查询,则判断子查询是否支持 DISTINCT
|
||||
Index relid = rel->relid;
|
||||
Query* subquery = root->simple_rte_array[relid]->subquery;
|
||||
List* colnos = NIL;
|
||||
|
|
@ -545,7 +567,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
* (XXX we are not considering restriction clauses attached to the
|
||||
* subquery; is that worth doing?)
|
||||
*/
|
||||
foreach (l, clause_list) {
|
||||
foreach (l, clause_list) { // 遍历子句列表,获取列号和操作符号
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(l);
|
||||
Oid op;
|
||||
Var* var = NULL;
|
||||
|
|
@ -567,7 +589,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
else
|
||||
var = (Var*)get_leftop(rinfo->clause);
|
||||
|
||||
if (var != NULL) {
|
||||
if (var != NULL) { // 尝试找到兼容的 Var
|
||||
/* try to find compatible var */
|
||||
var = locate_distribute_var((Expr*)var);
|
||||
}
|
||||
|
|
@ -584,7 +606,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
opids = lappend_oid(opids, op);
|
||||
}
|
||||
|
||||
if (query_is_distinct_for(subquery, colnos, opids))
|
||||
if (query_is_distinct_for(subquery, colnos, opids))// 调用 query_is_distinct_for 函数判断子查询是否支持 DISTINCT
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -601,7 +623,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
* query_is_distinct_for()'s argument lists if the call could not possibly
|
||||
* succeed.
|
||||
*/
|
||||
bool query_supports_distinctness(Query* query)
|
||||
bool query_supports_distinctness(Query* query)// 判断查询是否支持 DISTINCT 操作
|
||||
{
|
||||
if (query->distinctClause != NIL || query->groupClause != NIL || query->hasAggs || query->havingQual ||
|
||||
query->setOperations)
|
||||
|
|
@ -628,11 +650,12 @@ bool query_supports_distinctness(Query* query)
|
|||
* should give trustworthy answers for all operators that we might need
|
||||
* to deal with here.)
|
||||
*/
|
||||
bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
||||
bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查询是否对给定列列表 colnos 和操作符列表 opids 支持 DISTINCT 操作
|
||||
{
|
||||
ListCell* l = NULL;
|
||||
Oid opid;
|
||||
|
||||
// 断言:列列表和操作符列表长度必须相等
|
||||
Assert(list_length(colnos) == list_length(opids));
|
||||
|
||||
/*
|
||||
|
|
@ -643,7 +666,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* specified columns, since those must be evaluated before de-duplication;
|
||||
* but it doesn't presently seem worth the complication to check that.)
|
||||
*/
|
||||
if (expression_returns_set((Node*)query->targetList))
|
||||
if (expression_returns_set((Node*)query->targetList))// 如果查询的目标列表返回集合,则不支持 DISTINCT
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
@ -651,8 +674,9 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* columns in the DISTINCT clause appear in colnos and operator semantics
|
||||
* match.
|
||||
*/
|
||||
// 如果查询中存在 DISTINCT 子句
|
||||
if (query->distinctClause != NIL) {
|
||||
if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids))
|
||||
if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -660,15 +684,15 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* Similarly, GROUP BY guarantees uniqueness if all the grouped columns
|
||||
* appear in colnos and operator semantics match.
|
||||
*/
|
||||
if (query->groupClause != NIL && query->groupingSets == NIL) {
|
||||
if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids))
|
||||
if (query->groupClause != NIL && query->groupingSets == NIL) {// 如果查询中存在 GROUP BY 子句且没有 GROUPING SETS
|
||||
if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一
|
||||
return true;
|
||||
} else if (query->groupingSets != NIL) {
|
||||
/*
|
||||
* If we have grouping sets with expressions, we probably don't have
|
||||
* uniqueness and analysis would be hard. Punt.
|
||||
*/
|
||||
if (query->groupClause != NIL)
|
||||
if (query->groupClause != NIL) // 如果存在 GROUPING SETS
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
@ -677,13 +701,13 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* then we're returning only one row and are certainly unique. But
|
||||
* otherwise, we know we're certainly not unique.
|
||||
*/
|
||||
bool isTrue = list_length(query->groupingSets) == 1 &&
|
||||
bool isTrue = list_length(query->groupingSets) == 1 &&// 如果 GROUPING SETS 只包含一个空集合
|
||||
((GroupingSet*)linitial(query->groupingSets))->kind == GROUPING_SET_EMPTY;
|
||||
if (isTrue)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
} else {// 如果查询中存在聚合函数或 HAVING 子句,则支持 DISTINCT
|
||||
/*
|
||||
* If we have no GROUP BY, but do have aggregates or HAVING, then the
|
||||
* result is at most one row so it's surely unique, for any operators.
|
||||
|
|
@ -696,18 +720,19 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row,
|
||||
* except with ALL.
|
||||
*/
|
||||
// 如果查询中存在集合操作
|
||||
if (query->setOperations != NULL) {
|
||||
SetOperationStmt* topop = (SetOperationStmt*)query->setOperations;
|
||||
|
||||
Assert(IsA(topop, SetOperationStmt));
|
||||
Assert(topop->op != SETOP_NONE);
|
||||
|
||||
if (!topop->all) {
|
||||
if (!topop->all) {// 如果不是 UNION ALL 操作
|
||||
ListCell* lg = NULL;
|
||||
|
||||
/* We're good if all the nonjunk output columns are in colnos */
|
||||
lg = list_head(topop->groupClauses);
|
||||
foreach (l, query->targetList) {
|
||||
foreach (l, query->targetList) {// 遍历查询的目标列表
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(l);
|
||||
SortGroupClause* sgc = NULL;
|
||||
|
||||
|
|
@ -718,12 +743,13 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
Assert(lg != NULL);
|
||||
sgc = (SortGroupClause*)lfirst(lg);
|
||||
lg = lnext(lg);
|
||||
|
||||
// 调用 distinct_col_search 函数查找列的操作符
|
||||
opid = distinct_col_search(tle->resno, colnos, opids);
|
||||
if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop))
|
||||
break; /* exit early if no match */
|
||||
}
|
||||
if (l == NULL) /* had matches for all? */
|
||||
// 如果成功遍历了所有目标列,说明支持 DISTINCT
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -744,7 +770,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
|||
* Given group clause and targetlist, find if all the aggregated columns
|
||||
* in colnos, return true if so, else false.
|
||||
*/
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids)
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids)// 检查列是否唯一
|
||||
{
|
||||
ListCell* l = NULL;
|
||||
Oid opid;
|
||||
|
|
@ -754,9 +780,11 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c
|
|||
TargetEntry* tle = get_sortgroupclause_tle(sgc, targetList);
|
||||
|
||||
opid = distinct_col_search(tle->resno, colnos, opids);
|
||||
// 如果操作符无效或不兼容,则中断循环
|
||||
if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop))
|
||||
break; /* exit early if no match */
|
||||
}
|
||||
// 如果成功遍历了所有列,说明支持 DISTINCT
|
||||
if (l == NULL) /* had matches for all? */
|
||||
return true;
|
||||
|
||||
|
|
@ -770,15 +798,15 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c
|
|||
* else return InvalidOid. (Ordinarily colnos would not contain duplicates,
|
||||
* but if it does, we arbitrarily select the first match.)
|
||||
*/
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids)
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids)// 在列列表 colnos 中查找列的操作符
|
||||
{
|
||||
ListCell* lc1 = NULL;
|
||||
ListCell* lc2 = NULL;
|
||||
|
||||
forboth(lc1, colnos, lc2, opids)
|
||||
forboth(lc1, colnos, lc2, opids) // 同时遍历列列表和操作符列表
|
||||
{
|
||||
if (colno == lfirst_int(lc1))
|
||||
return lfirst_oid(lc2);
|
||||
}
|
||||
return InvalidOid;
|
||||
return InvalidOid;// 如果未找到,返回无效的操作符标识
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue