diff options
Diffstat (limited to 'src/backend/optimizer/path/joinrels.c')
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 86 |
1 files changed, 80 insertions, 6 deletions
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 1a01ae9b70e..4c705048147 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -224,11 +224,14 @@ join_search_one_level(PlannerInfo *root, int level) * to accept failure at level 4 and go on to discover a workable * bushy plan at level 5. * - * However, if there are no special joins then join_is_legal() should - * never fail, and so the following sanity check is useful. + * However, if there are no special joins and no lateral references + * then join_is_legal() should never fail, and so the following sanity + * check is useful. *---------- */ - if (joinrels[level] == NIL && root->join_info_list == NIL) + if (joinrels[level] == NIL && + root->join_info_list == NIL && + root->lateral_info_list == NIL) elog(ERROR, "failed to build any %d-way joins", level); } } @@ -329,6 +332,8 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, bool reversed; bool unique_ified; bool is_valid_inner; + bool lateral_fwd; + bool lateral_rev; ListCell *l; /* @@ -508,6 +513,47 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, (match_sjinfo == NULL || unique_ified)) return false; /* invalid join path */ + /* + * We also have to check for constraints imposed by LATERAL references. + * The proposed rels could each contain lateral references to the other, + * in which case the join is impossible. If there are lateral references + * in just one direction, then the join has to be done with a nestloop + * with the lateral referencer on the inside. If the join matches an SJ + * that cannot be implemented by such a nestloop, the join is impossible. + */ + lateral_fwd = lateral_rev = false; + 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)) + { + /* has to be implemented as nestloop with rel1 on left */ + if (lateral_rev) + return false; /* have lateral refs in both directions */ + lateral_fwd = true; + if (!bms_is_subset(ljinfo->lateral_lhs, rel1->relids)) + return false; /* rel1 can't compute the required parameter */ + if (match_sjinfo && + (reversed || match_sjinfo->jointype == JOIN_FULL)) + return false; /* not implementable as nestloop */ + } + if (bms_is_member(ljinfo->lateral_rhs, rel1->relids) && + bms_overlap(ljinfo->lateral_lhs, rel2->relids)) + { + /* has to be implemented as nestloop with rel2 on left */ + if (lateral_fwd) + return false; /* have lateral refs in both directions */ + lateral_rev = true; + if (!bms_is_subset(ljinfo->lateral_lhs, rel2->relids)) + return false; /* rel2 can't compute the required parameter */ + if (match_sjinfo && + (!reversed || match_sjinfo->jointype == JOIN_FULL)) + return false; /* not implementable as nestloop */ + } + } + /* Otherwise, it's a valid join */ *sjinfo_p = match_sjinfo; *reversed_p = reversed; @@ -752,12 +798,14 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) /* * have_join_order_restriction * Detect whether the two relations should be joined to satisfy - * a join-order restriction arising from special joins. + * a join-order restriction arising from special or lateral joins. * * In practice this is always used with have_relevant_joinclause(), and so * could be merged with that function, but it seems clearer to separate the * two concerns. We need this test because there are degenerate cases where * a clauseless join must be performed to satisfy join-order restrictions. + * Also, if one rel has a lateral reference to the other, we should consider + * joining them even if the join would be clauseless. * * Note: this is only a problem if one side of a degenerate outer join * contains multiple rels, or a clauseless join is required within an @@ -774,6 +822,22 @@ have_join_order_restriction(PlannerInfo *root, ListCell *l; /* + * If either side has a lateral reference to the other, attempt the join + * regardless of outer-join considerations. + */ + 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; + } + + /* * It's possible that the rels correspond to the left and right sides of a * degenerate outer join, that is, one with no joinclause mentioning the * non-nullable side; in which case we should force the join to occur. @@ -846,8 +910,9 @@ have_join_order_restriction(PlannerInfo *root, /* * has_join_restriction - * Detect whether the specified relation has join-order restrictions - * due to being inside an outer join or an IN (sub-SELECT). + * Detect whether the specified relation has join-order restrictions, + * due to being inside an outer join or an IN (sub-SELECT), + * or participating in any LATERAL references. * * Essentially, this tests whether have_join_order_restriction() could * succeed with this rel and some other one. It's OK if we sometimes @@ -859,6 +924,15 @@ has_join_restriction(PlannerInfo *root, RelOptInfo *rel) { ListCell *l; + 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); |