From 5c491cebe1d97de43d1eb00a8d2c76b7e2345bad Mon Sep 17 00:00:00 2001 From: Geng Lei Date: Fri, 13 Nov 2026 10:19:42 +0000 Subject: [PATCH] Fix bug related to join_clause_is_movable_into() --- src/backend/optimizer/util/pathnode.c | 3 +- src/backend/optimizer/util/relnode.c | 43 ++++++++-------- src/backend/optimizer/util/restrictinfo.c | 63 +++++++++++++++++++++++ src/include/optimizer/restrictinfo.h | 5 ++ 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 3df00829e68..38650f41d98 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -2446,7 +2446,8 @@ create_nestloop_path(PlannerInfo *root, { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - if (!join_clause_is_movable_into(rinfo, + if (!join_clause_is_movable_onto(root, + rinfo, inner_path->parent->relids, inner_and_outer)) jclauses = lappend(jclauses, rinfo); diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 591306735a5..5d9bb9945eb 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -1447,13 +1447,16 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - if (join_clause_is_movable_into(rinfo, + if (join_clause_is_movable_onto(root, + rinfo, joinrel->relids, join_and_req) && - !join_clause_is_movable_into(rinfo, + !join_clause_is_movable_onto(root, + rinfo, outer_path->parent->relids, outer_and_req) && - !join_clause_is_movable_into(rinfo, + !join_clause_is_movable_onto(root, + rinfo, inner_path->parent->relids, inner_and_req)) pclauses = lappend(pclauses, rinfo); @@ -1470,23 +1473,20 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - /* - * In principle, join_clause_is_movable_into() should accept anything - * returned by generate_join_implied_equalities(); but because its - * analysis is only approximate, sometimes it doesn't. So we - * currently cannot use this Assert; instead just assume it's okay to - * apply the joinclause at this level. - */ -#ifdef NOT_USED - Assert(join_clause_is_movable_into(rinfo, - joinrel->relids, - join_and_req)); -#endif - if (join_clause_is_movable_into(rinfo, + /* If the rinfo deduced from EC is not movable to joinrel, skip it */ + if(!join_clause_is_movable_onto(root, + rinfo, + joinrel->relids, + join_and_req)) + continue; + + if (join_clause_is_movable_onto(root, + rinfo, outer_path->parent->relids, outer_and_req)) continue; /* drop if movable into LHS */ - if (join_clause_is_movable_into(rinfo, + if (join_clause_is_movable_onto(root, + rinfo, inner_path->parent->relids, inner_and_req)) { @@ -1544,13 +1544,12 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - /* As above, can't quite assert this here */ -#ifdef NOT_USED - Assert(join_clause_is_movable_into(rinfo, + Assert(join_clause_is_movable_onto(root, + rinfo, outer_path->parent->relids, real_outer_and_req)); -#endif - if (!join_clause_is_movable_into(rinfo, + if (!join_clause_is_movable_onto(root, + rinfo, outer_path->parent->relids, outer_and_req)) pclauses = lappend(pclauses, rinfo); diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index e7764f43091..2d960ff5485 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -653,3 +653,66 @@ join_clause_is_movable_into(RestrictInfo *rinfo, return true; } + +/* + * If currentrelids is a joinrel, join_clause_is_movable_into() may be overly + * restrictive, keeping some clause movable to a parameterized path from + * being moved down. For example, consider this query: + * select * from a, b left join lateral(select a4+b4 x, * from c) SQ on b1=c1 + * where a2=c2; + * At the top level query, the path of SQ is parameterized by both a and b. The + * paths of joinrel (b left join SQ) are all parameterized by a. The clause + * a2=c2 is movable to the paths of joinrel (b left join SQ), however, + * join_clause_is_movable_into() judges it as not movable due to SQ is on + * the nullable side of left join. join_clause_is_movable_onto() loosen the + * restriction to allow the movable clauses to be moved. + */ +bool +join_clause_is_movable_onto(PlannerInfo *root, RestrictInfo *rinfo, + Relids currentrelids, + Relids current_and_outer) +{ + ListCell *l; + /* Clause must be evaluable given available context */ + if (!bms_is_subset(rinfo->clause_relids, current_and_outer)) + return false; + /* Clause must physically reference at least one target rel */ + if (!bms_overlap(currentrelids, rinfo->clause_relids)) + return false; + + /* Cannot move an outer-join clause into the join's outer side */ + if (bms_overlap(currentrelids, rinfo->outer_relids)) + return false; + /* + * If there is any special join whose min_righthand overlaps currentrelids, + * and currentrelids don't contain both its min_lefthand and min_righthand, + * rinfo can't be moved, otherwise the nullable side of the special join + * will undergo premature filtering, resulting incorrect query result. + * Furthermore, if the special join is a full join, its min_lefthand + * overlaps currentrelids, and currentrelids don't contain both its + * min_lefthand and min_righthand, rinfo can't be moved too. + */ + foreach(l, root->join_info_list) + { + SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l); + if ((bms_overlap(sjinfo->min_righthand, currentrelids) && + !bms_overlap(sjinfo->min_lefthand, currentrelids)) || + (sjinfo->jointype == JOIN_FULL && + bms_overlap(sjinfo->min_lefthand, currentrelids) && + !bms_overlap(sjinfo->min_righthand, currentrelids))) + { + if (!bms_is_subset(rinfo->required_relids, + bms_union(sjinfo->min_lefthand,sjinfo->min_righthand))) + return false; + if (bms_is_subset(rinfo->required_relids, sjinfo->min_righthand)) + continue; + if(sjinfo->jointype == JOIN_FULL && + bms_is_subset(rinfo->required_relids, sjinfo->min_righthand)) + continue; + if(rinfo->is_pushed_down) + return false; + } + } + return true; +} + diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index fd7a0ec69df..e7a2a230c81 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -47,5 +47,10 @@ extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel); extern bool join_clause_is_movable_into(RestrictInfo *rinfo, Relids currentrelids, Relids current_and_outer); +extern bool join_clause_is_movable_onto(PlannerInfo *root, + RestrictInfo *rinfo, + Relids currentrelids, + Relids current_and_outer) ; + #endif /* RESTRICTINFO_H */ base-commit: 78d0dd090ae6e944a569b79afb27971b49406eb3 -- 2.53.0