mirror of
https://github.com/postgres/postgres.git
synced 2026-05-19 16:57:03 -04:00
Clean up remove_rel_from_query() after self-join elimination commit
The self-join elimination (SJE) commit grafted self-join removal onto remove_rel_from_query(), which was originally written for left-join removal only. This resulted in several issues: - Comments throughout remove_rel_from_query() still assumed only left-join removal, making the code misleading. - ChangeVarNodesExtended was called on phv->phexpr with subst=-1 during left-join removal, which is pointless and confusing since any surviving PHV shouldn't reference the removed rel. - phinfo->ph_lateral was adjusted for left-join removal, which is unnecessary since the removed relid cannot appear in ph_lateral for outer joins. - The comment about attr_needed reconstruction was in remove_rel_from_query(), but the actual rebuild is performed by the callers. - EquivalenceClass processing in remove_rel_from_query() is redundant for self-join removal, since the caller (remove_self_join_rel) already handles ECs via update_eclasses(). - In remove_self_join_rel(), ChangeVarNodesExtended was called on root->processed_groupClause, which contains SortGroupClause nodes that have no Var nodes to rewrite. The accompanying comment incorrectly mentioned "HAVING clause". This patch fixes all these issues, clarifying the separation between left-join removal and self-join elimination code paths within remove_rel_from_query(). The resulting code is also better structured for adding new types of join removal (such as inner-join removal) in the future. Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: Tender Wang <tndrwang@gmail.com> Reviewed-by: Andrei Lepikhov <lepihov@gmail.com> Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com> Discussion: https://postgr.es/m/CAMbWs48JC4OVqE=3gMB6se2WmRNNfMyFyYxm-09vgpm+Vwe8Hg@mail.gmail.com
This commit is contained in:
parent
04f9ea372a
commit
20efbdffeb
1 changed files with 257 additions and 248 deletions
|
|
@ -57,11 +57,13 @@ bool enable_self_join_elimination;
|
|||
static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
|
||||
static void remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
|
||||
SpecialJoinInfo *sjinfo);
|
||||
static void remove_rel_from_query(PlannerInfo *root, int relid,
|
||||
int subst, SpecialJoinInfo *sjinfo,
|
||||
Relids joinrelids);
|
||||
static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
|
||||
int relid, int ojrelid);
|
||||
static void remove_rel_from_eclass(EquivalenceClass *ec,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
int relid, int subst);
|
||||
int relid, int ojrelid);
|
||||
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,
|
||||
|
|
@ -312,227 +314,6 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove the target rel->relid and references to the target join from the
|
||||
* planner's data structures, having determined that there is no need
|
||||
* to include them in the query. Optionally replace them with subst if subst
|
||||
* is non-negative.
|
||||
*
|
||||
* This function updates only parts needed for both left-join removal and
|
||||
* self-join removal.
|
||||
*/
|
||||
static void
|
||||
remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
|
||||
int subst, SpecialJoinInfo *sjinfo,
|
||||
Relids joinrelids)
|
||||
{
|
||||
int relid = rel->relid;
|
||||
Index rti;
|
||||
ListCell *l;
|
||||
|
||||
/*
|
||||
* Update all_baserels and related relid sets.
|
||||
*/
|
||||
root->all_baserels = adjust_relid_set(root->all_baserels, relid, subst);
|
||||
root->all_query_rels = adjust_relid_set(root->all_query_rels, relid, subst);
|
||||
|
||||
if (sjinfo != NULL)
|
||||
{
|
||||
root->outer_join_rels = bms_del_member(root->outer_join_rels,
|
||||
sjinfo->ojrelid);
|
||||
root->all_query_rels = bms_del_member(root->all_query_rels,
|
||||
sjinfo->ojrelid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from SpecialJoinInfo data structures.
|
||||
*
|
||||
* This is relevant in case the outer join we're deleting is nested inside
|
||||
* other outer joins: the upper joins' relid sets have to be adjusted. The
|
||||
* 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)
|
||||
{
|
||||
SpecialJoinInfo *sjinf = (SpecialJoinInfo *) lfirst(l);
|
||||
|
||||
/*
|
||||
* initsplan.c is fairly cavalier about allowing SpecialJoinInfos'
|
||||
* lefthand/righthand relid sets to be shared with other data
|
||||
* structures. Ensure that we don't modify the original relid sets.
|
||||
* (The commute_xxx sets are always per-SpecialJoinInfo though.)
|
||||
*/
|
||||
sjinf->min_lefthand = bms_copy(sjinf->min_lefthand);
|
||||
sjinf->min_righthand = bms_copy(sjinf->min_righthand);
|
||||
sjinf->syn_lefthand = bms_copy(sjinf->syn_lefthand);
|
||||
sjinf->syn_righthand = bms_copy(sjinf->syn_righthand);
|
||||
/* Now remove relid from the sets: */
|
||||
sjinf->min_lefthand = adjust_relid_set(sjinf->min_lefthand, relid, subst);
|
||||
sjinf->min_righthand = adjust_relid_set(sjinf->min_righthand, relid, subst);
|
||||
sjinf->syn_lefthand = adjust_relid_set(sjinf->syn_lefthand, relid, subst);
|
||||
sjinf->syn_righthand = adjust_relid_set(sjinf->syn_righthand, relid, subst);
|
||||
|
||||
if (sjinfo != NULL)
|
||||
{
|
||||
Assert(subst <= 0);
|
||||
|
||||
/* Remove sjinfo->ojrelid bits from the sets: */
|
||||
sjinf->min_lefthand = bms_del_member(sjinf->min_lefthand,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->min_righthand = bms_del_member(sjinf->min_righthand,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->syn_lefthand = bms_del_member(sjinf->syn_lefthand,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->syn_righthand = bms_del_member(sjinf->syn_righthand,
|
||||
sjinfo->ojrelid);
|
||||
/* relid cannot appear in these fields, but ojrelid can: */
|
||||
sjinf->commute_above_l = bms_del_member(sjinf->commute_above_l,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->commute_above_r = bms_del_member(sjinf->commute_above_r,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->commute_below_l = bms_del_member(sjinf->commute_below_l,
|
||||
sjinfo->ojrelid);
|
||||
sjinf->commute_below_r = bms_del_member(sjinf->commute_below_r,
|
||||
sjinfo->ojrelid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(subst > 0);
|
||||
|
||||
ChangeVarNodesExtended((Node *) sjinf->semi_rhs_exprs, relid, subst,
|
||||
0, replace_relid_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from PlaceHolderVar data structures,
|
||||
* removing any no-longer-needed placeholders entirely. We remove PHV
|
||||
* only for left-join removal. With self-join elimination, PHVs already
|
||||
* get moved to the remaining relation, where they might still be needed.
|
||||
* It might also happen that we skip the removal of some PHVs that could
|
||||
* be removed. However, the overhead of extra PHVs is small compared to
|
||||
* the complexity of analysis needed to remove them.
|
||||
*
|
||||
* Removal is a bit trickier than it might seem: we can remove PHVs that
|
||||
* are used at the target rel and/or in the join qual, but not those that
|
||||
* are used at join partner rels or above the join. It's not that easy to
|
||||
* distinguish PHVs used at partner rels from those used in the join qual,
|
||||
* since they will both have ph_needed sets that are subsets of
|
||||
* joinrelids. However, a PHV used at a partner rel could not have the
|
||||
* target rel in ph_eval_at, so we check that while deciding whether to
|
||||
* remove or just update the PHV. There is no corresponding test in
|
||||
* join_is_removable because it doesn't need to distinguish those cases.
|
||||
*/
|
||||
foreach(l, root->placeholder_list)
|
||||
{
|
||||
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
|
||||
|
||||
Assert(sjinfo == NULL || !bms_is_member(relid, phinfo->ph_lateral));
|
||||
if (sjinfo != NULL &&
|
||||
bms_is_subset(phinfo->ph_needed, joinrelids) &&
|
||||
bms_is_member(relid, phinfo->ph_eval_at) &&
|
||||
!bms_is_member(sjinfo->ojrelid, phinfo->ph_eval_at))
|
||||
{
|
||||
/*
|
||||
* This code shouldn't be executed if one relation is substituted
|
||||
* with another: in this case, the placeholder may be employed in
|
||||
* a filter inside the scan node the SJE removes.
|
||||
*/
|
||||
root->placeholder_list = foreach_delete_current(root->placeholder_list,
|
||||
l);
|
||||
root->placeholder_array[phinfo->phid] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaceHolderVar *phv = phinfo->ph_var;
|
||||
|
||||
phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at, relid, subst);
|
||||
if (sjinfo != NULL)
|
||||
phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at,
|
||||
sjinfo->ojrelid, subst);
|
||||
Assert(!bms_is_empty(phinfo->ph_eval_at)); /* checked previously */
|
||||
/* Reduce ph_needed to contain only "relation 0"; see below */
|
||||
if (bms_is_member(0, phinfo->ph_needed))
|
||||
phinfo->ph_needed = bms_make_singleton(0);
|
||||
else
|
||||
phinfo->ph_needed = NULL;
|
||||
|
||||
phinfo->ph_lateral = adjust_relid_set(phinfo->ph_lateral, relid, subst);
|
||||
|
||||
/*
|
||||
* ph_lateral might contain rels mentioned in ph_eval_at after the
|
||||
* replacement, remove them.
|
||||
*/
|
||||
phinfo->ph_lateral = bms_difference(phinfo->ph_lateral, phinfo->ph_eval_at);
|
||||
/* ph_lateral might or might not be empty */
|
||||
|
||||
phv->phrels = adjust_relid_set(phv->phrels, relid, subst);
|
||||
if (sjinfo != NULL)
|
||||
phv->phrels = adjust_relid_set(phv->phrels,
|
||||
sjinfo->ojrelid, subst);
|
||||
Assert(!bms_is_empty(phv->phrels));
|
||||
|
||||
ChangeVarNodesExtended((Node *) phv->phexpr, relid, subst, 0,
|
||||
replace_relid_callback);
|
||||
|
||||
Assert(phv->phnullingrels == NULL); /* no need to adjust */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from EquivalenceClasses.
|
||||
*/
|
||||
foreach(l, root->eq_classes)
|
||||
{
|
||||
EquivalenceClass *ec = (EquivalenceClass *) lfirst(l);
|
||||
|
||||
if (bms_is_member(relid, ec->ec_relids) ||
|
||||
(sjinfo == NULL || bms_is_member(sjinfo->ojrelid, ec->ec_relids)))
|
||||
remove_rel_from_eclass(ec, sjinfo, relid, subst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, we must recompute per-Var attr_needed and per-PlaceHolderVar
|
||||
* ph_needed relid sets. These have to be known accurately, else we may
|
||||
* fail to remove other now-removable outer joins. And our removal of the
|
||||
* join clause(s) for this outer join may mean that Vars that were
|
||||
* formerly needed no longer are. So we have to do this honestly by
|
||||
* repeating the construction of those relid sets. We can cheat to one
|
||||
* small extent: we can avoid re-examining the targetlist and HAVING qual
|
||||
* by preserving "relation 0" bits from the existing relid sets. This is
|
||||
* safe because we'd never remove such references.
|
||||
*
|
||||
* So, start by removing all other bits from attr_needed sets and
|
||||
* lateral_vars lists. (We already did this above for ph_needed.)
|
||||
*/
|
||||
for (rti = 1; rti < root->simple_rel_array_size; rti++)
|
||||
{
|
||||
RelOptInfo *otherrel = root->simple_rel_array[rti];
|
||||
int attroff;
|
||||
|
||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||
if (otherrel == NULL)
|
||||
continue;
|
||||
|
||||
Assert(otherrel->relid == rti); /* sanity check on array */
|
||||
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr;
|
||||
attroff >= 0;
|
||||
attroff--)
|
||||
{
|
||||
if (bms_is_member(0, otherrel->attr_needed[attroff]))
|
||||
otherrel->attr_needed[attroff] = bms_make_singleton(0);
|
||||
else
|
||||
otherrel->attr_needed[attroff] = NULL;
|
||||
}
|
||||
|
||||
if (subst > 0)
|
||||
ChangeVarNodesExtended((Node *) otherrel->lateral_vars, relid,
|
||||
subst, 0, replace_relid_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the target relid and references to the target join from the
|
||||
* planner's data structures, having determined that there is no need
|
||||
|
|
@ -557,7 +338,7 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
|
|||
Assert(ojrelid != 0);
|
||||
joinrelids = bms_add_member(joinrelids, ojrelid);
|
||||
|
||||
remove_rel_from_query(root, rel, -1, sjinfo, joinrelids);
|
||||
remove_rel_from_query(root, relid, -1, sjinfo, joinrelids);
|
||||
|
||||
/*
|
||||
* Remove any joinquals referencing the rel from the joininfo lists.
|
||||
|
|
@ -647,6 +428,248 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
|
|||
rebuild_lateral_attr_needed(root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the target relid and references to the target join from the
|
||||
* planner's data structures, having determined that there is no need
|
||||
* to include them in the query. Optionally replace references to the
|
||||
* removed relid with subst if this is a self-join removal.
|
||||
*
|
||||
* This function serves as the common infrastructure for left-join removal
|
||||
* and self-join elimination. It is intentionally scoped to update only the
|
||||
* shared planner data structures that are universally affected by relation
|
||||
* removal. Each specific caller remains responsible for updating any
|
||||
* remaining data structures required by its unique removal logic.
|
||||
*
|
||||
* The specific type of removal being performed is dictated by the combination
|
||||
* of the sjinfo and subst parameters. A non-NULL sjinfo indicates left-join
|
||||
* removal. When sjinfo is NULL, a positive subst value indicates self-join
|
||||
* elimination (where references are replaced with subst).
|
||||
*/
|
||||
static void
|
||||
remove_rel_from_query(PlannerInfo *root, int relid,
|
||||
int subst, SpecialJoinInfo *sjinfo,
|
||||
Relids joinrelids)
|
||||
{
|
||||
int ojrelid = sjinfo ? sjinfo->ojrelid : 0;
|
||||
Index rti;
|
||||
ListCell *l;
|
||||
bool is_outer_join = (sjinfo != NULL);
|
||||
bool is_self_join = (!is_outer_join && subst > 0);
|
||||
|
||||
Assert(is_outer_join || is_self_join);
|
||||
Assert(!is_outer_join || ojrelid > 0);
|
||||
Assert(!is_outer_join || joinrelids != NULL);
|
||||
|
||||
/*
|
||||
* Update all_baserels and related relid sets.
|
||||
*/
|
||||
root->all_baserels = adjust_relid_set(root->all_baserels, relid, subst);
|
||||
root->all_query_rels = adjust_relid_set(root->all_query_rels, relid, subst);
|
||||
|
||||
if (is_outer_join)
|
||||
{
|
||||
root->outer_join_rels = bms_del_member(root->outer_join_rels, ojrelid);
|
||||
root->all_query_rels = bms_del_member(root->all_query_rels, ojrelid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from SpecialJoinInfo data structures.
|
||||
*
|
||||
* This is relevant in case the relation we're deleting is part of the
|
||||
* relid sets of special joins: those sets have to be adjusted. If we are
|
||||
* removing an outer join, the RHS of the target outer join will be made
|
||||
* empty here, but that's OK since the caller will delete that
|
||||
* SpecialJoinInfo entirely.
|
||||
*/
|
||||
foreach(l, root->join_info_list)
|
||||
{
|
||||
SpecialJoinInfo *sjinf = (SpecialJoinInfo *) lfirst(l);
|
||||
|
||||
/*
|
||||
* initsplan.c is fairly cavalier about allowing SpecialJoinInfos'
|
||||
* lefthand/righthand relid sets to be shared with other data
|
||||
* structures. Ensure that we don't modify the original relid sets.
|
||||
* (The commute_xxx sets are always per-SpecialJoinInfo though.)
|
||||
*/
|
||||
sjinf->min_lefthand = bms_copy(sjinf->min_lefthand);
|
||||
sjinf->min_righthand = bms_copy(sjinf->min_righthand);
|
||||
sjinf->syn_lefthand = bms_copy(sjinf->syn_lefthand);
|
||||
sjinf->syn_righthand = bms_copy(sjinf->syn_righthand);
|
||||
|
||||
/* Now adjust relid bit in the sets: */
|
||||
sjinf->min_lefthand = adjust_relid_set(sjinf->min_lefthand, relid, subst);
|
||||
sjinf->min_righthand = adjust_relid_set(sjinf->min_righthand, relid, subst);
|
||||
sjinf->syn_lefthand = adjust_relid_set(sjinf->syn_lefthand, relid, subst);
|
||||
sjinf->syn_righthand = adjust_relid_set(sjinf->syn_righthand, relid, subst);
|
||||
|
||||
if (is_outer_join)
|
||||
{
|
||||
/* Remove ojrelid bit from the sets: */
|
||||
sjinf->min_lefthand = bms_del_member(sjinf->min_lefthand, ojrelid);
|
||||
sjinf->min_righthand = bms_del_member(sjinf->min_righthand, ojrelid);
|
||||
sjinf->syn_lefthand = bms_del_member(sjinf->syn_lefthand, ojrelid);
|
||||
sjinf->syn_righthand = bms_del_member(sjinf->syn_righthand, ojrelid);
|
||||
/* relid cannot appear in these fields, but ojrelid can: */
|
||||
sjinf->commute_above_l = bms_del_member(sjinf->commute_above_l, ojrelid);
|
||||
sjinf->commute_above_r = bms_del_member(sjinf->commute_above_r, ojrelid);
|
||||
sjinf->commute_below_l = bms_del_member(sjinf->commute_below_l, ojrelid);
|
||||
sjinf->commute_below_r = bms_del_member(sjinf->commute_below_r, ojrelid);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* For self-join removal, replace relid references in
|
||||
* semi_rhs_exprs.
|
||||
*/
|
||||
ChangeVarNodesExtended((Node *) sjinf->semi_rhs_exprs, relid, subst,
|
||||
0, replace_relid_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from PlaceHolderVar data structures,
|
||||
* removing any no-longer-needed placeholders entirely. We only remove
|
||||
* PHVs for left-join removal. With self-join elimination, PHVs already
|
||||
* get moved to the remaining relation, where they might still be needed.
|
||||
* It might also happen that we skip the removal of some PHVs that could
|
||||
* be removed. However, the overhead of extra PHVs is small compared to
|
||||
* the complexity of analysis needed to remove them.
|
||||
*
|
||||
* Removal is a bit trickier than it might seem: we can remove PHVs that
|
||||
* are used at the target rel and/or in the join qual, but not those that
|
||||
* are used at join partner rels or above the join. It's not that easy to
|
||||
* distinguish PHVs used at partner rels from those used in the join qual,
|
||||
* since they will both have ph_needed sets that are subsets of
|
||||
* joinrelids. However, a PHV used at a partner rel could not have the
|
||||
* target rel in ph_eval_at, so we check that while deciding whether to
|
||||
* remove or just update the PHV. There is no corresponding test in
|
||||
* join_is_removable because it doesn't need to distinguish those cases.
|
||||
*/
|
||||
foreach(l, root->placeholder_list)
|
||||
{
|
||||
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
|
||||
|
||||
Assert(!is_outer_join || !bms_is_member(relid, phinfo->ph_lateral));
|
||||
|
||||
if (is_outer_join &&
|
||||
bms_is_subset(phinfo->ph_needed, joinrelids) &&
|
||||
bms_is_member(relid, phinfo->ph_eval_at) &&
|
||||
!bms_is_member(ojrelid, phinfo->ph_eval_at))
|
||||
{
|
||||
root->placeholder_list = foreach_delete_current(root->placeholder_list,
|
||||
l);
|
||||
root->placeholder_array[phinfo->phid] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaceHolderVar *phv = phinfo->ph_var;
|
||||
|
||||
phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at, relid, subst);
|
||||
if (is_outer_join)
|
||||
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, ojrelid);
|
||||
Assert(!bms_is_empty(phinfo->ph_eval_at)); /* checked previously */
|
||||
|
||||
/* Reduce ph_needed to contain only "relation 0"; see below */
|
||||
if (bms_is_member(0, phinfo->ph_needed))
|
||||
phinfo->ph_needed = bms_make_singleton(0);
|
||||
else
|
||||
phinfo->ph_needed = NULL;
|
||||
|
||||
phv->phrels = adjust_relid_set(phv->phrels, relid, subst);
|
||||
if (is_outer_join)
|
||||
phv->phrels = bms_del_member(phv->phrels, ojrelid);
|
||||
Assert(!bms_is_empty(phv->phrels));
|
||||
|
||||
/*
|
||||
* For self-join removal, update Var nodes within the PHV's
|
||||
* expression to reference the replacement relid, and adjust
|
||||
* ph_lateral for the relid substitution. (For left-join removal,
|
||||
* we're removing rather than replacing, and any surviving PHV
|
||||
* shouldn't reference the removed rel in its expression. Also,
|
||||
* relid can't appear in ph_lateral for outer joins.)
|
||||
*/
|
||||
if (is_self_join)
|
||||
{
|
||||
ChangeVarNodesExtended((Node *) phv->phexpr, relid, subst, 0,
|
||||
replace_relid_callback);
|
||||
phinfo->ph_lateral = adjust_relid_set(phinfo->ph_lateral, relid, subst);
|
||||
|
||||
/*
|
||||
* ph_lateral might contain rels mentioned in ph_eval_at after
|
||||
* the replacement, remove them.
|
||||
*/
|
||||
phinfo->ph_lateral = bms_difference(phinfo->ph_lateral, phinfo->ph_eval_at);
|
||||
/* ph_lateral might or might not be empty */
|
||||
}
|
||||
|
||||
Assert(phv->phnullingrels == NULL); /* no need to adjust */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise remove references from EquivalenceClasses.
|
||||
*
|
||||
* For self-join removal, the caller has already updated the
|
||||
* EquivalenceClasses, so we can skip this step.
|
||||
*/
|
||||
if (is_outer_join)
|
||||
{
|
||||
foreach(l, root->eq_classes)
|
||||
{
|
||||
EquivalenceClass *ec = (EquivalenceClass *) lfirst(l);
|
||||
|
||||
if (bms_is_member(relid, ec->ec_relids) ||
|
||||
bms_is_member(ojrelid, ec->ec_relids))
|
||||
remove_rel_from_eclass(ec, relid, ojrelid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, we must prepare for the caller to recompute per-Var
|
||||
* attr_needed and per-PlaceHolderVar ph_needed relid sets. These have to
|
||||
* be known accurately, else we may fail to remove other now-removable
|
||||
* joins. Because the caller removes the join clause(s) associated with
|
||||
* the removed join, Vars that were formerly needed may no longer be.
|
||||
*
|
||||
* The actual reconstruction of these relid sets is performed by the
|
||||
* specific caller. Here, we simply clear out the existing attr_needed
|
||||
* sets (we already did this above for ph_needed) to ensure they are
|
||||
* rebuilt from scratch. We can cheat to one small extent: we can avoid
|
||||
* re-examining the targetlist and HAVING qual by preserving "relation 0"
|
||||
* bits from the existing relid sets. This is safe because we'd never
|
||||
* remove such references.
|
||||
*
|
||||
* Additionally, if we are performing self-join elimination, we must
|
||||
* replace references to the removed relid with subst within the
|
||||
* lateral_vars lists.
|
||||
*/
|
||||
for (rti = 1; rti < root->simple_rel_array_size; rti++)
|
||||
{
|
||||
RelOptInfo *otherrel = root->simple_rel_array[rti];
|
||||
int attroff;
|
||||
|
||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||
if (otherrel == NULL)
|
||||
continue;
|
||||
|
||||
Assert(otherrel->relid == rti); /* sanity check on array */
|
||||
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr;
|
||||
attroff >= 0;
|
||||
attroff--)
|
||||
{
|
||||
if (bms_is_member(0, otherrel->attr_needed[attroff]))
|
||||
otherrel->attr_needed[attroff] = bms_make_singleton(0);
|
||||
else
|
||||
otherrel->attr_needed[attroff] = NULL;
|
||||
}
|
||||
|
||||
if (is_self_join)
|
||||
ChangeVarNodesExtended((Node *) otherrel->lateral_vars, relid,
|
||||
subst, 0, replace_relid_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any references to relid or ojrelid from the RestrictInfo.
|
||||
*
|
||||
|
|
@ -707,8 +730,7 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
|
|||
}
|
||||
|
||||
/*
|
||||
* Remove any references to relid or sjinfo->ojrelid (if sjinfo != NULL)
|
||||
* from the EquivalenceClass.
|
||||
* Remove any references to relid or ojrelid from the EquivalenceClass.
|
||||
*
|
||||
* Like remove_rel_from_restrictinfo, we don't worry about cleaning out
|
||||
* any nullingrel bits in contained Vars and PHVs. (This might have to be
|
||||
|
|
@ -717,16 +739,13 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
|
|||
* level(s).
|
||||
*/
|
||||
static void
|
||||
remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
|
||||
int relid, int subst)
|
||||
remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
/* Fix up the EC's overall relids */
|
||||
ec->ec_relids = adjust_relid_set(ec->ec_relids, relid, subst);
|
||||
if (sjinfo != NULL)
|
||||
ec->ec_relids = adjust_relid_set(ec->ec_relids,
|
||||
sjinfo->ojrelid, subst);
|
||||
ec->ec_relids = bms_del_member(ec->ec_relids, relid);
|
||||
ec->ec_relids = bms_del_member(ec->ec_relids, ojrelid);
|
||||
|
||||
/*
|
||||
* We don't expect any EC child members to exist at this point. Ensure
|
||||
|
|
@ -745,14 +764,11 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
|
|||
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
|
||||
|
||||
if (bms_is_member(relid, cur_em->em_relids) ||
|
||||
(sjinfo != NULL && bms_is_member(sjinfo->ojrelid,
|
||||
cur_em->em_relids)))
|
||||
bms_is_member(ojrelid, cur_em->em_relids))
|
||||
{
|
||||
Assert(!cur_em->em_is_const);
|
||||
cur_em->em_relids = adjust_relid_set(cur_em->em_relids, relid, subst);
|
||||
if (sjinfo != NULL)
|
||||
cur_em->em_relids = adjust_relid_set(cur_em->em_relids,
|
||||
sjinfo->ojrelid, subst);
|
||||
cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
|
||||
cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
|
||||
if (bms_is_empty(cur_em->em_relids))
|
||||
ec->ec_members = foreach_delete_current(ec->ec_members, lc);
|
||||
}
|
||||
|
|
@ -763,11 +779,7 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
|
|||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
|
||||
if (sjinfo == NULL)
|
||||
ChangeVarNodesExtended((Node *) rinfo, relid, subst, 0,
|
||||
replace_relid_callback);
|
||||
else
|
||||
remove_rel_from_restrictinfo(rinfo, relid, sjinfo->ojrelid);
|
||||
remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1958,14 +1970,11 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
|
|||
0, replace_relid_callback);
|
||||
|
||||
/* Replace links in the planner info */
|
||||
remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
|
||||
remove_rel_from_query(root, toRemove->relid, toKeep->relid, NULL, NULL);
|
||||
|
||||
/* At last, replace varno in root targetlist and HAVING clause */
|
||||
/* Replace varno in the fully-processed targetlist */
|
||||
ChangeVarNodesExtended((Node *) root->processed_tlist, toRemove->relid,
|
||||
toKeep->relid, 0, replace_relid_callback);
|
||||
ChangeVarNodesExtended((Node *) root->processed_groupClause,
|
||||
toRemove->relid, toKeep->relid, 0,
|
||||
replace_relid_callback);
|
||||
|
||||
adjust_relid_set(root->all_result_relids, toRemove->relid, toKeep->relid);
|
||||
adjust_relid_set(root->leaf_result_relids, toRemove->relid, toKeep->relid);
|
||||
|
|
|
|||
Loading…
Reference in a new issue