Skip to content

Commit 719012e

Browse files
committed
Add some defenses against constant-FALSE outer join conditions. Since
eval_const_expressions will generally throw away anything that's ANDed with constant FALSE, what we're left with given an example like select * from tenk1 a where (unique1,0) in (select unique2,1 from tenk1 b); is a cartesian product computation, which is really not acceptable. This is a regression in CVS HEAD compared to previous releases, which were able to notice the impossible join condition in this case --- though not in some related cases that are also improved by this patch, such as select * from tenk1 a left join tenk1 b on (a.unique1=b.unique2 and 0=1); Fix by skipping evaluation of the appropriate side of the outer join in cases where it's demonstrably unnecessary.
1 parent f2689e4 commit 719012e

File tree

1 file changed

+64
-15
lines changed

1 file changed

+64
-15
lines changed

src/backend/optimizer/path/joinrels.c

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.93 2008/08/14 18:47:59 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.94 2008/08/17 19:40:11 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -28,7 +28,8 @@ static List *make_rels_by_clauseless_joins(PlannerInfo *root,
2828
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
2929
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
3030
static bool is_dummy_rel(RelOptInfo *rel);
31-
static void mark_dummy_join(RelOptInfo *rel);
31+
static void mark_dummy_rel(RelOptInfo *rel);
32+
static bool restriction_is_constant_false(List *restrictlist);
3233

3334

3435
/*
@@ -558,15 +559,20 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
558559
* this way since it's conceivable that dummy-ness of a multi-element
559560
* join might only be noticeable for certain construction paths.)
560561
*
562+
* Also, a provably constant-false join restriction typically means that
563+
* we can skip evaluating one or both sides of the join. We do this
564+
* by marking the appropriate rel as dummy.
565+
*
561566
* We need only consider the jointypes that appear in join_info_list,
562567
* plus JOIN_INNER.
563568
*/
564569
switch (sjinfo->jointype)
565570
{
566571
case JOIN_INNER:
567-
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
572+
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
573+
restriction_is_constant_false(restrictlist))
568574
{
569-
mark_dummy_join(joinrel);
575+
mark_dummy_rel(joinrel);
570576
break;
571577
}
572578
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -579,9 +585,12 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
579585
case JOIN_LEFT:
580586
if (is_dummy_rel(rel1))
581587
{
582-
mark_dummy_join(joinrel);
588+
mark_dummy_rel(joinrel);
583589
break;
584590
}
591+
if (restriction_is_constant_false(restrictlist) &&
592+
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
593+
mark_dummy_rel(rel2);
585594
add_paths_to_joinrel(root, joinrel, rel1, rel2,
586595
JOIN_LEFT, sjinfo,
587596
restrictlist);
@@ -592,7 +601,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
592601
case JOIN_FULL:
593602
if (is_dummy_rel(rel1) && is_dummy_rel(rel2))
594603
{
595-
mark_dummy_join(joinrel);
604+
mark_dummy_rel(joinrel);
596605
break;
597606
}
598607
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -603,9 +612,10 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
603612
restrictlist);
604613
break;
605614
case JOIN_SEMI:
606-
if (is_dummy_rel(rel1) || is_dummy_rel(rel2))
615+
if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
616+
restriction_is_constant_false(restrictlist))
607617
{
608-
mark_dummy_join(joinrel);
618+
mark_dummy_rel(joinrel);
609619
break;
610620
}
611621
add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -632,9 +642,12 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
632642
case JOIN_ANTI:
633643
if (is_dummy_rel(rel1))
634644
{
635-
mark_dummy_join(joinrel);
645+
mark_dummy_rel(joinrel);
636646
break;
637647
}
648+
if (restriction_is_constant_false(restrictlist) &&
649+
bms_is_subset(rel2->relids, sjinfo->syn_righthand))
650+
mark_dummy_rel(rel2);
638651
add_paths_to_joinrel(root, joinrel, rel1, rel2,
639652
JOIN_ANTI, sjinfo,
640653
restrictlist);
@@ -851,10 +864,10 @@ is_dummy_rel(RelOptInfo *rel)
851864
}
852865

853866
/*
854-
* Mark a joinrel as proven empty.
867+
* Mark a rel as proven empty.
855868
*/
856869
static void
857-
mark_dummy_join(RelOptInfo *rel)
870+
mark_dummy_rel(RelOptInfo *rel)
858871
{
859872
/* Set dummy size estimate */
860873
rel->rows = 0;
@@ -865,10 +878,46 @@ mark_dummy_join(RelOptInfo *rel)
865878
/* Set up the dummy path */
866879
add_path(rel, (Path *) create_append_path(rel, NIL));
867880

881+
/* Set or update cheapest_total_path */
882+
set_cheapest(rel);
883+
}
884+
885+
886+
/*
887+
* restriction_is_constant_false --- is a restrictlist just FALSE?
888+
*
889+
* In cases where a qual is provably constant FALSE, eval_const_expressions
890+
* will generally have thrown away anything that's ANDed with it. In outer
891+
* join situations this will leave us computing cartesian products only to
892+
* decide there's no match for an outer row, which is pretty stupid. So,
893+
* we need to detect the case.
894+
*/
895+
static bool
896+
restriction_is_constant_false(List *restrictlist)
897+
{
898+
ListCell *lc;
899+
868900
/*
869-
* Although set_cheapest will be done again later, we do it immediately
870-
* in order to keep is_dummy_rel as cheap as possible (ie, not have
871-
* to examine the pathlist).
901+
* Despite the above comment, the restriction list we see here might
902+
* possibly have other members besides the FALSE constant, since other
903+
* quals could get "pushed down" to the outer join level. So we check
904+
* each member of the list.
872905
*/
873-
set_cheapest(rel);
906+
foreach(lc, restrictlist)
907+
{
908+
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
909+
910+
Assert(IsA(rinfo, RestrictInfo));
911+
if (rinfo->clause && IsA(rinfo->clause, Const))
912+
{
913+
Const *con = (Const *) rinfo->clause;
914+
915+
/* constant NULL is as good as constant FALSE for our purposes */
916+
if (con->constisnull)
917+
return true;
918+
if (!DatumGetBool(con->constvalue))
919+
return true;
920+
}
921+
}
922+
return false;
874923
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy