Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8124215

Browse files
committed
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.
1 parent b307d7a commit 8124215

File tree

1 file changed

+58
-30
lines changed

1 file changed

+58
-30
lines changed

src/backend/optimizer/plan/initsplan.c

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.123 2006/10/04 00:29:54 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.124 2006/12/07 19:33:40 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -735,30 +735,69 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
735735
* For a non-outer-join qual, we can evaluate the qual as soon as (1)
736736
* we have all the rels it mentions, and (2) we are at or above any
737737
* outer joins that can null any of these rels and are below the
738-
* syntactic location of the given qual. To enforce the latter, scan
739-
* the oj_info_list and merge the required-relid sets of any such OJs
740-
* into the clause's own reference list. At the time we are called,
741-
* the oj_info_list contains only outer joins below this qual.
738+
* syntactic location of the given qual. We must enforce (2) because
739+
* pushing down such a clause below the OJ might cause the OJ to emit
740+
* null-extended rows that should not have been formed, or that should
741+
* have been rejected by the clause. (This is only an issue for
742+
* non-strict quals, since if we can prove a qual mentioning only
743+
* nullable rels is strict, we'd have reduced the outer join to an
744+
* inner join in reduce_outer_joins().)
745+
*
746+
* To enforce (2), scan the oj_info_list and merge the required-relid
747+
* sets of any such OJs into the clause's own reference list. At the
748+
* time we are called, the oj_info_list contains only outer joins
749+
* below this qual. We have to repeat the scan until no new relids
750+
* get added; this ensures that the qual is suitably delayed regardless
751+
* of the order in which OJs get executed. As an example, if we have
752+
* one OJ with LHS=A, RHS=B, and one with LHS=B, RHS=C, it is implied
753+
* that these can be done in either order; if the B/C join is done
754+
* first then the join to A can null C, so a qual actually mentioning
755+
* only C cannot be applied below the join to A.
742756
*/
743-
Relids addrelids = NULL;
744-
ListCell *l;
757+
bool found_some;
745758

746759
outerjoin_delayed = false;
747-
foreach(l, root->oj_info_list)
748-
{
749-
OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l);
760+
do {
761+
ListCell *l;
750762

751-
if (bms_overlap(relids, ojinfo->min_righthand) ||
752-
(ojinfo->is_full_join &&
753-
bms_overlap(relids, ojinfo->min_lefthand)))
763+
found_some = false;
764+
foreach(l, root->oj_info_list)
754765
{
755-
addrelids = bms_add_members(addrelids, ojinfo->min_lefthand);
756-
addrelids = bms_add_members(addrelids, ojinfo->min_righthand);
757-
outerjoin_delayed = true;
766+
OuterJoinInfo *ojinfo = (OuterJoinInfo *) lfirst(l);
767+
768+
/* do we have any nullable rels of this OJ? */
769+
if (bms_overlap(relids, ojinfo->min_righthand) ||
770+
(ojinfo->is_full_join &&
771+
bms_overlap(relids, ojinfo->min_lefthand)))
772+
{
773+
/* yes; do we have all its rels? */
774+
if (!bms_is_subset(ojinfo->min_lefthand, relids) ||
775+
!bms_is_subset(ojinfo->min_righthand, relids))
776+
{
777+
/* no, so add them in */
778+
relids = bms_add_members(relids,
779+
ojinfo->min_lefthand);
780+
relids = bms_add_members(relids,
781+
ojinfo->min_righthand);
782+
outerjoin_delayed = true;
783+
/* we'll need another iteration */
784+
found_some = true;
785+
}
786+
}
758787
}
759-
}
788+
} while (found_some);
760789

761-
if (bms_is_subset(addrelids, relids))
790+
if (outerjoin_delayed)
791+
{
792+
/* Should still be a subset of current scope ... */
793+
Assert(bms_is_subset(relids, qualscope));
794+
/*
795+
* Because application of the qual will be delayed by outer join,
796+
* we mustn't assume its vars are equal everywhere.
797+
*/
798+
maybe_equijoin = false;
799+
}
800+
else
762801
{
763802
/*
764803
* Qual is not delayed by any lower outer-join restriction. If it
@@ -774,19 +813,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
774813
else
775814
maybe_equijoin = false;
776815
}
777-
else
778-
{
779-
relids = bms_union(relids, addrelids);
780-
/* Should still be a subset of current scope ... */
781-
Assert(bms_is_subset(relids, qualscope));
782816

783-
/*
784-
* Because application of the qual will be delayed by outer join,
785-
* we mustn't assume its vars are equal everywhere.
786-
*/
787-
maybe_equijoin = false;
788-
}
789-
bms_free(addrelids);
817+
/* Since it doesn't mention the LHS, it's certainly not an OJ clause */
790818
maybe_outer_join = false;
791819
}
792820

0 commit comments

Comments
 (0)