diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2015-12-07 17:41:45 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2015-12-07 17:41:45 -0500 |
commit | 25517ee14c1a018876b64dce73e8f7fb7e937783 (patch) | |
tree | 52f4bcc40e3b1d4dce4e4b535b46cd7a4f752f17 /src/backend/optimizer/path | |
parent | 9d1839fad945cba7e23e645a3c212f34e56495f7 (diff) | |
download | postgresql-25517ee14c1a018876b64dce73e8f7fb7e937783.tar.gz postgresql-25517ee14c1a018876b64dce73e8f7fb7e937783.zip |
Fix another oversight in checking if a join with LATERAL refs is legal.
It was possible for the planner to decide to join a LATERAL subquery to
the outer side of an outer join before the outer join itself is completed.
Normally that's fine because of the associativity rules, but it doesn't
work if the subquery contains a lateral reference to the inner side of the
outer join. In such a situation the outer join *must* be done first.
join_is_legal() missed this consideration and would allow the join to be
attempted, but the actual path-building code correctly decided that no
valid join path could be made, sometimes leading to planner errors such as
"failed to build any N-way joins".
Per report from Andreas Seltenreich. Back-patch to 9.3 where LATERAL
support was added.
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index b2cc9f07f56..9f0212fad23 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -334,6 +334,7 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, bool must_be_leftjoin; bool lateral_fwd; bool lateral_rev; + Relids join_lateral_rels; ListCell *l; /* @@ -569,6 +570,35 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, } } + /* + * LATERAL references could also cause problems later on if we accept this + * join: if the join's minimum parameterization includes any rels that + * would have to be on the inside of an outer join with this join rel, + * then it's never going to be possible to build the complete query using + * this join. We should reject this join not only because it'll save + * work, but because if we don't, the clauseless-join heuristics might + * think that legality of this join means that some other join rel need + * not be formed, and that could lead to failure to find any plan at all. + * It seems best not to merge this check into the main loop above, because + * it is concerned with SJs that are not otherwise relevant to this join. + */ + join_lateral_rels = min_join_parameterization(root, joinrelids); + if (join_lateral_rels) + { + foreach(l, root->join_info_list) + { + SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l); + + if (bms_overlap(sjinfo->min_righthand, join_lateral_rels) && + bms_overlap(sjinfo->min_lefthand, joinrelids)) + return false; /* will not be able to join to min_righthand */ + if (sjinfo->jointype == JOIN_FULL && + bms_overlap(sjinfo->min_lefthand, join_lateral_rels) && + bms_overlap(sjinfo->min_righthand, joinrelids)) + return false; /* will not be able to join to min_lefthand */ + } + } + /* Otherwise, it's a valid join */ *sjinfo_p = match_sjinfo; *reversed_p = reversed; |