Skip to content

Commit 250a718

Browse files
committed
Refactor ChangeVarNodesExtended() using the custom callback
fc069a3 implemented Self-Join Elimination (SJE) and put related logic to ChangeVarNodes_walker(). This commit provides refactoring to remove the SJE-related logic from ChangeVarNodes_walker() but adds a custom callback to ChangeVarNodesExtended(), which has a chance to process a node before ChangeVarNodes_walker(). Passing this callback to ChangeVarNodesExtended() allows SJE-related node handling to be kept within the analyzejoins.c. Reported-by: Richard Guo <guofenglinux@gmail.com> Discussion: https://postgr.es/m/CAMbWs49PE3CvnV8vrQ0Dr%3DHqgZZmX0tdNbzVNJxqc8yg-8kDQQ%40mail.gmail.com Author: Andrei Lepikhov <lepihov@gmail.com> Author: Alexander Korotkov <aekorotkov@gmail.com>
1 parent fb21ed6 commit 250a718

File tree

4 files changed

+192
-124
lines changed

4 files changed

+192
-124
lines changed

src/backend/optimizer/plan/analyzejoins.c

Lines changed: 145 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ static bool is_innerrel_unique_for(PlannerInfo *root,
7474
List *restrictlist,
7575
List **extra_clauses);
7676
static int self_join_candidates_cmp(const void *a, const void *b);
77+
static bool replace_relid_callback(Node *node,
78+
ChangeVarNodes_context *context);
7779

7880

7981
/*
@@ -397,7 +399,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
397399
{
398400
Assert(subst > 0);
399401

400-
ChangeVarNodes((Node *) sjinf->semi_rhs_exprs, relid, subst, 0);
402+
ChangeVarNodesExtended((Node *) sjinf->semi_rhs_exprs, relid, subst,
403+
0, replace_relid_callback);
401404
}
402405
}
403406

@@ -469,7 +472,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
469472
sjinfo->ojrelid, subst);
470473
Assert(!bms_is_empty(phv->phrels));
471474

472-
ChangeVarNodes((Node *) phv->phexpr, relid, subst, 0);
475+
ChangeVarNodesExtended((Node *) phv->phexpr, relid, subst, 0,
476+
replace_relid_callback);
473477

474478
Assert(phv->phnullingrels == NULL); /* no need to adjust */
475479
}
@@ -523,7 +527,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
523527
}
524528

525529
if (subst > 0)
526-
ChangeVarNodes((Node *) otherrel->lateral_vars, relid, subst, 0);
530+
ChangeVarNodesExtended((Node *) otherrel->lateral_vars, relid,
531+
subst, 0, replace_relid_callback);
527532
}
528533
}
529534

@@ -757,7 +762,8 @@ remove_rel_from_eclass(EquivalenceClass *ec, SpecialJoinInfo *sjinfo,
757762
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
758763

759764
if (sjinfo == NULL)
760-
ChangeVarNodes((Node *) rinfo, relid, subst, 0);
765+
ChangeVarNodesExtended((Node *) rinfo, relid, subst, 0,
766+
replace_relid_callback);
761767
else
762768
remove_rel_from_restrictinfo(rinfo, relid, sjinfo->ojrelid);
763769
}
@@ -1548,7 +1554,8 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
15481554
em->em_jdomain->jd_relids = adjust_relid_set(em->em_jdomain->jd_relids, from, to);
15491555

15501556
/* We only process inner joins */
1551-
ChangeVarNodes((Node *) em->em_expr, from, to, 0);
1557+
ChangeVarNodesExtended((Node *) em->em_expr, from, to, 0,
1558+
replace_relid_callback);
15521559

15531560
foreach_node(EquivalenceMember, other, new_members)
15541561
{
@@ -1582,7 +1589,8 @@ update_eclasses(EquivalenceClass *ec, int from, int to)
15821589
continue;
15831590
}
15841591

1585-
ChangeVarNodes((Node *) rinfo, from, to, 0);
1592+
ChangeVarNodesExtended((Node *) rinfo, from, to, 0,
1593+
replace_relid_callback);
15861594

15871595
/*
15881596
* After switching the clause to the remaining relation, check it for
@@ -1677,6 +1685,118 @@ add_non_redundant_clauses(PlannerInfo *root,
16771685
}
16781686
}
16791687

1688+
/*
1689+
* A custom callback for ChangeVarNodesExtended() providing
1690+
* Self-join elimination (SJE) related functionality
1691+
*
1692+
* SJE needs to skip the RangeTblRef node
1693+
* type. During SJE's last step, remove_rel_from_joinlist() removes
1694+
* remaining RangeTblRefs with target relid. If ChangeVarNodes() replaces
1695+
* the target relid before, remove_rel_from_joinlist() fails to identify
1696+
* the nodes to delete.
1697+
*
1698+
* SJE also needs to change the relids within RestrictInfo's.
1699+
*/
1700+
static bool
1701+
replace_relid_callback(Node *node, ChangeVarNodes_context *context)
1702+
{
1703+
if (IsA(node, RangeTblRef))
1704+
{
1705+
return true;
1706+
}
1707+
else if (IsA(node, RestrictInfo))
1708+
{
1709+
RestrictInfo *rinfo = (RestrictInfo *) node;
1710+
int relid = -1;
1711+
bool is_req_equal =
1712+
(rinfo->required_relids == rinfo->clause_relids);
1713+
bool clause_relids_is_multiple =
1714+
(bms_membership(rinfo->clause_relids) == BMS_MULTIPLE);
1715+
1716+
/*
1717+
* Recurse down into clauses if the target relation is present in
1718+
* clause_relids or required_relids. We must check required_relids
1719+
* because the relation not present in clause_relids might still be
1720+
* present somewhere in orclause.
1721+
*/
1722+
if (bms_is_member(context->rt_index, rinfo->clause_relids) ||
1723+
bms_is_member(context->rt_index, rinfo->required_relids))
1724+
{
1725+
Relids new_clause_relids;
1726+
1727+
ChangeVarNodesWalkExpression((Node *) rinfo->clause, context);
1728+
ChangeVarNodesWalkExpression((Node *) rinfo->orclause, context);
1729+
1730+
new_clause_relids = adjust_relid_set(rinfo->clause_relids,
1731+
context->rt_index,
1732+
context->new_index);
1733+
1734+
/*
1735+
* Incrementally adjust num_base_rels based on the change of
1736+
* clause_relids, which could contain both base relids and
1737+
* outer-join relids. This operation is legal until we remove
1738+
* only baserels.
1739+
*/
1740+
rinfo->num_base_rels -= bms_num_members(rinfo->clause_relids) -
1741+
bms_num_members(new_clause_relids);
1742+
1743+
rinfo->clause_relids = new_clause_relids;
1744+
rinfo->left_relids =
1745+
adjust_relid_set(rinfo->left_relids, context->rt_index, context->new_index);
1746+
rinfo->right_relids =
1747+
adjust_relid_set(rinfo->right_relids, context->rt_index, context->new_index);
1748+
}
1749+
1750+
if (is_req_equal)
1751+
rinfo->required_relids = rinfo->clause_relids;
1752+
else
1753+
rinfo->required_relids =
1754+
adjust_relid_set(rinfo->required_relids, context->rt_index, context->new_index);
1755+
1756+
rinfo->outer_relids =
1757+
adjust_relid_set(rinfo->outer_relids, context->rt_index, context->new_index);
1758+
rinfo->incompatible_relids =
1759+
adjust_relid_set(rinfo->incompatible_relids, context->rt_index, context->new_index);
1760+
1761+
if (rinfo->mergeopfamilies &&
1762+
bms_get_singleton_member(rinfo->clause_relids, &relid) &&
1763+
clause_relids_is_multiple &&
1764+
relid == context->new_index && IsA(rinfo->clause, OpExpr))
1765+
{
1766+
Expr *leftOp;
1767+
Expr *rightOp;
1768+
1769+
leftOp = (Expr *) get_leftop(rinfo->clause);
1770+
rightOp = (Expr *) get_rightop(rinfo->clause);
1771+
1772+
/*
1773+
* For self-join elimination, changing varnos could transform
1774+
* "t1.a = t2.a" into "t1.a = t1.a". That is always true as long
1775+
* as "t1.a" is not null. We use qual() to check for such a case,
1776+
* and then we replace the qual for a check for not null
1777+
* (NullTest).
1778+
*/
1779+
if (leftOp != NULL && equal(leftOp, rightOp))
1780+
{
1781+
NullTest *ntest = makeNode(NullTest);
1782+
1783+
ntest->arg = leftOp;
1784+
ntest->nulltesttype = IS_NOT_NULL;
1785+
ntest->argisrow = false;
1786+
ntest->location = -1;
1787+
rinfo->clause = (Expr *) ntest;
1788+
rinfo->mergeopfamilies = NIL;
1789+
rinfo->left_em = NULL;
1790+
rinfo->right_em = NULL;
1791+
}
1792+
Assert(rinfo->orclause == NULL);
1793+
}
1794+
return true;
1795+
}
1796+
1797+
return false;
1798+
}
1799+
16801800
/*
16811801
* Remove a relation after we have proven that it participates only in an
16821802
* unneeded unique self-join.
@@ -1725,7 +1845,8 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
17251845
foreach_node(RestrictInfo, rinfo, joininfos)
17261846
{
17271847
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
1728-
ChangeVarNodes((Node *) rinfo, toRemove->relid, toKeep->relid, 0);
1848+
ChangeVarNodesExtended((Node *) rinfo, toRemove->relid, toKeep->relid,
1849+
0, replace_relid_callback);
17291850

17301851
if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
17311852
jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1743,7 +1864,8 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
17431864
restrictlist);
17441865
foreach_node(RestrictInfo, rinfo, toRemove->baserestrictinfo)
17451866
{
1746-
ChangeVarNodes((Node *) rinfo, toRemove->relid, toKeep->relid, 0);
1867+
ChangeVarNodesExtended((Node *) rinfo, toRemove->relid, toKeep->relid,
1868+
0, replace_relid_callback);
17471869

17481870
if (bms_membership(rinfo->required_relids) == BMS_MULTIPLE)
17491871
jinfo_candidates = lappend(jinfo_candidates, rinfo);
@@ -1785,7 +1907,8 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
17851907
{
17861908
Node *node = lfirst(lc);
17871909

1788-
ChangeVarNodes(node, toRemove->relid, toKeep->relid, 0);
1910+
ChangeVarNodesExtended(node, toRemove->relid, toKeep->relid, 0,
1911+
replace_relid_callback);
17891912
if (!list_member(toKeep->reltarget->exprs, node))
17901913
toKeep->reltarget->exprs = lappend(toKeep->reltarget->exprs, node);
17911914
}
@@ -1829,14 +1952,18 @@ remove_self_join_rel(PlannerInfo *root, PlanRowMark *kmark, PlanRowMark *rmark,
18291952
* Replace varno in all the query structures, except nodes RangeTblRef
18301953
* otherwise later remove_rel_from_joinlist will yield errors.
18311954
*/
1832-
ChangeVarNodesExtended((Node *) root->parse, toRemove->relid, toKeep->relid, 0, false);
1955+
ChangeVarNodesExtended((Node *) root->parse, toRemove->relid, toKeep->relid,
1956+
0, replace_relid_callback);
18331957

18341958
/* Replace links in the planner info */
18351959
remove_rel_from_query(root, toRemove, toKeep->relid, NULL, NULL);
18361960

18371961
/* At last, replace varno in root targetlist and HAVING clause */
1838-
ChangeVarNodes((Node *) root->processed_tlist, toRemove->relid, toKeep->relid, 0);
1839-
ChangeVarNodes((Node *) root->processed_groupClause, toRemove->relid, toKeep->relid, 0);
1962+
ChangeVarNodesExtended((Node *) root->processed_tlist, toRemove->relid,
1963+
toKeep->relid, 0, replace_relid_callback);
1964+
ChangeVarNodesExtended((Node *) root->processed_groupClause,
1965+
toRemove->relid, toKeep->relid, 0,
1966+
replace_relid_callback);
18401967

18411968
adjust_relid_set(root->all_result_relids, toRemove->relid, toKeep->relid);
18421969
adjust_relid_set(root->leaf_result_relids, toRemove->relid, toKeep->relid);
@@ -1919,8 +2046,10 @@ split_selfjoin_quals(PlannerInfo *root, List *joinquals, List **selfjoinquals,
19192046
* when we have cast of the same var to different (but compatible)
19202047
* types.
19212048
*/
1922-
ChangeVarNodes(rightexpr, bms_singleton_member(rinfo->right_relids),
1923-
bms_singleton_member(rinfo->left_relids), 0);
2049+
ChangeVarNodesExtended(rightexpr,
2050+
bms_singleton_member(rinfo->right_relids),
2051+
bms_singleton_member(rinfo->left_relids), 0,
2052+
replace_relid_callback);
19242053

19252054
if (equal(leftexpr, rightexpr))
19262055
sjoinquals = lappend(sjoinquals, rinfo);
@@ -1956,7 +2085,8 @@ match_unique_clauses(PlannerInfo *root, RelOptInfo *outer, List *uclauses,
19562085
bms_is_empty(rinfo->right_relids));
19572086

19582087
clause = (Expr *) copyObject(rinfo->clause);
1959-
ChangeVarNodes((Node *) clause, relid, outer->relid, 0);
2088+
ChangeVarNodesExtended((Node *) clause, relid, outer->relid, 0,
2089+
replace_relid_callback);
19602090

19612091
iclause = bms_is_empty(rinfo->left_relids) ? get_rightop(clause) :
19622092
get_leftop(clause);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy