Commit 8124215c authored by Tom Lane's avatar Tom Lane

Repair incorrect placement of WHERE clauses when there are multiple,

rearrangeable outer joins and the WHERE clause is non-strict and mentions
only nullable-side relations.  New bug in 8.2, caused by new logic to allow
rearranging outer joins.  Per bug #2807 from Ross Cohen; thanks to Jeff
Davis for producing a usable test case.
parent b307d7a6
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.123 2006/10/04 00:29:54 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.124 2006/12/07 19:33:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -735,30 +735,69 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -735,30 +735,69 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* For a non-outer-join qual, we can evaluate the qual as soon as (1) * For a non-outer-join qual, we can evaluate the qual as soon as (1)
* we have all the rels it mentions, and (2) we are at or above any * we have all the rels it mentions, and (2) we are at or above any
* outer joins that can null any of these rels and are below the * outer joins that can null any of these rels and are below the
* syntactic location of the given qual. To enforce the latter, scan * syntactic location of the given qual. We must enforce (2) because
* the oj_info_list and merge the required-relid sets of any such OJs * pushing down such a clause below the OJ might cause the OJ to emit
* into the clause's own reference list. At the time we are called, * null-extended rows that should not have been formed, or that should
* the oj_info_list contains only outer joins below this qual. * have been rejected by the clause. (This is only an issue for
* non-strict quals, since if we can prove a qual mentioning only
* nullable rels is strict, we'd have reduced the outer join to an
* inner join in reduce_outer_joins().)
*
* To enforce (2), scan the oj_info_list and merge the required-relid
* sets of any such OJs into the clause's own reference list. At the
* time we are called, the oj_info_list contains only outer joins
* below this qual. We have to repeat the scan until no new relids
* get added; this ensures that the qual is suitably delayed regardless
* of the order in which OJs get executed. As an example, if we have
* one OJ with LHS=A, RHS=B, and one with LHS=B, RHS=C, it is implied
* that these can be done in either order; if the B/C join is done
* first then the join to A can null C, so a qual actually mentioning
* only C cannot be applied below the join to A.
*/ */
Relids addrelids = NULL; bool found_some;
ListCell *l;
outerjoin_delayed = false; outerjoin_delayed = false;
foreach(l, root->oj_info_list) do {
{ ListCell *l;
OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l);
if (bms_overlap(relids, ojinfo->min_righthand) || found_some = false;
(ojinfo->is_full_join && foreach(l, root->oj_info_list)
bms_overlap(relids, ojinfo->min_lefthand)))
{ {
addrelids = bms_add_members(addrelids, ojinfo->min_lefthand); OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l);
addrelids = bms_add_members(addrelids, ojinfo->min_righthand);
outerjoin_delayed = true; /* do we have any nullable rels of this OJ? */
if (bms_overlap(relids, ojinfo->min_righthand) ||
(ojinfo->is_full_join &&
bms_overlap(relids, ojinfo->min_lefthand)))
{
/* yes; do we have all its rels? */
if (!bms_is_subset(ojinfo->min_lefthand, relids) ||
!bms_is_subset(ojinfo->min_righthand, relids))
{
/* no, so add them in */
relids = bms_add_members(relids,
ojinfo->min_lefthand);
relids = bms_add_members(relids,
ojinfo->min_righthand);
outerjoin_delayed = true;
/* we'll need another iteration */
found_some = true;
}
}
} }
} } while (found_some);
if (bms_is_subset(addrelids, relids)) if (outerjoin_delayed)
{
/* Should still be a subset of current scope ... */
Assert(bms_is_subset(relids, qualscope));
/*
* Because application of the qual will be delayed by outer join,
* we mustn't assume its vars are equal everywhere.
*/
maybe_equijoin = false;
}
else
{ {
/* /*
* Qual is not delayed by any lower outer-join restriction. If it * Qual is not delayed by any lower outer-join restriction. If it
...@@ -774,19 +813,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -774,19 +813,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
else else
maybe_equijoin = false; maybe_equijoin = false;
} }
else
{
relids = bms_union(relids, addrelids);
/* Should still be a subset of current scope ... */
Assert(bms_is_subset(relids, qualscope));
/* /* Since it doesn't mention the LHS, it's certainly not an OJ clause */
* Because application of the qual will be delayed by outer join,
* we mustn't assume its vars are equal everywhere.
*/
maybe_equijoin = false;
}
bms_free(addrelids);
maybe_outer_join = false; maybe_outer_join = false;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment