@ -8,7 +8,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / optimizer / path / joinrels . c , v 1.76 .2 .1 2005 / 11 / 22 18 : 23 : 10 momjian Exp $
* $ PostgreSQL : pgsql / src / backend / optimizer / path / joinrels . c , v 1.76 .2 .2 2007 / 02 / 16 00 : 14 : 16 tgl Exp $
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
*/
@ -25,7 +25,7 @@ static List *make_rels_by_clause_joins(PlannerInfo *root,
static List * make_rels_by_clauseless_joins ( PlannerInfo * root ,
static List * make_rels_by_clauseless_joins ( PlannerInfo * root ,
RelOptInfo * old_rel ,
RelOptInfo * old_rel ,
ListCell * other_rels ) ;
ListCell * other_rels ) ;
static bool is_inside_IN ( PlannerInfo * root , RelOptInfo * rel ) ;
static bool has_join_restriction ( PlannerInfo * root , RelOptInfo * rel ) ;
/*
/*
@ -72,7 +72,7 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
other_rels = list_head ( joinrels [ 1 ] ) ; /* consider all initial
other_rels = list_head ( joinrels [ 1 ] ) ; /* consider all initial
* rels */
* rels */
if ( old_rel - > joininfo ! = NIL )
if ( old_rel - > joininfo ! = NIL | | has_join_restriction ( root , old_rel ) )
{
{
/*
/*
* Note that if all available join clauses for this rel require
* Note that if all available join clauses for this rel require
@ -80,30 +80,19 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
* it here . In most cases that ' s OK ; it ' ll be considered by
* it here . In most cases that ' s OK ; it ' ll be considered by
* " bushy plan " join code in a higher - level pass where we have
* " bushy plan " join code in a higher - level pass where we have
* those other rels collected into a join rel .
* those other rels collected into a join rel .
*
* See also the last - ditch case below .
*/
*/
new_rels = make_rels_by_clause_joins ( root ,
new_rels = make_rels_by_clause_joins ( root ,
old_rel ,
old_rel ,
other_rels ) ;
other_rels ) ;
/*
* An exception occurs when there is a clauseless join inside an
* IN ( sub - SELECT ) construct . Here , the members of the subselect
* all have join clauses ( against the stuff outside the IN ) , but
* they * must * be joined to each other before we can make use of
* those join clauses . So do the clauseless join bit .
*
* See also the last - ditch case below .
*/
if ( new_rels = = NIL & & is_inside_IN ( root , old_rel ) )
new_rels = make_rels_by_clauseless_joins ( root ,
old_rel ,
other_rels ) ;
}
}
else
else
{
{
/*
/*
* Oops , we have a relation that is not joined to any other
* Oops , we have a relation that is not joined to any other
* relation . Cartesian product time .
* relation , either directly or by join - order restrictions .
* Cartesian product time .
*/
*/
new_rels = make_rels_by_clauseless_joins ( root ,
new_rels = make_rels_by_clauseless_joins ( root ,
old_rel ,
old_rel ,
@ -126,8 +115,8 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
* joined to relations of level - k initial rels , for 2 < = k < = level - 2.
* joined to relations of level - k initial rels , for 2 < = k < = level - 2.
*
*
* We only consider bushy - plan joins for pairs of rels where there is a
* We only consider bushy - plan joins for pairs of rels where there is a
* suitable join clause , in order to avoid unreasonable growth of planning
* suitable join clause ( or join order restriction ) , in order to avoid
* time .
* unreasonable growth of planning time .
*/
*/
for ( k = 2 ; ; k + + )
for ( k = 2 ; ; k + + )
{
{
@ -146,8 +135,14 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
ListCell * other_rels ;
ListCell * other_rels ;
ListCell * r2 ;
ListCell * r2 ;
if ( old_rel - > joininfo = = NIL )
/*
continue ; /* we ignore clauseless joins here */
* We can ignore clauseless joins here , * except * when they
* participate in join - order restrictions - - - then we might have
* to force a bushy join plan .
*/
if ( old_rel - > joininfo = = NIL & &
! has_join_restriction ( root , old_rel ) )
continue ;
if ( k = = other_level )
if ( k = = other_level )
other_rels = lnext ( r ) ; /* only consider remaining rels */
other_rels = lnext ( r ) ; /* only consider remaining rels */
@ -163,9 +158,10 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
/*
/*
* OK , we can build a rel of the right level from this
* OK , we can build a rel of the right level from this
* pair of rels . Do so if there is at least one usable
* pair of rels . Do so if there is at least one usable
* join clause .
* join clause or a relevant join restriction .
*/
*/
if ( have_relevant_joinclause ( old_rel , new_rel ) )
if ( have_relevant_joinclause ( old_rel , new_rel ) | |
have_join_order_restriction ( root , old_rel , new_rel ) )
{
{
RelOptInfo * jrel ;
RelOptInfo * jrel ;
@ -245,8 +241,8 @@ make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
/*
/*
* make_rels_by_clause_joins
* make_rels_by_clause_joins
* Build joins between the given relation ' old_rel ' and other relations
* Build joins between the given relation ' old_rel ' and other relations
* that are mentioned within old_rel ' s joininfo list ( i . e . , relations
* that participate in join clauses that ' old_rel ' also participates in
* that participate in join clauses that ' old_rel ' also participates in ) .
* ( or participate in join - order restrictions with it ) .
* The join rel nodes are returned in a list .
* The join rel nodes are returned in a list .
*
*
* ' old_rel ' is the relation entry for the relation to be joined
* ' old_rel ' is the relation entry for the relation to be joined
@ -269,7 +265,8 @@ make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo * other_rel = ( RelOptInfo * ) lfirst ( l ) ;
RelOptInfo * other_rel = ( RelOptInfo * ) lfirst ( l ) ;
if ( ! bms_overlap ( old_rel - > relids , other_rel - > relids ) & &
if ( ! bms_overlap ( old_rel - > relids , other_rel - > relids ) & &
have_relevant_joinclause ( old_rel , other_rel ) )
( have_relevant_joinclause ( old_rel , other_rel ) | |
have_join_order_restriction ( root , old_rel , other_rel ) ) )
{
{
RelOptInfo * jrel ;
RelOptInfo * jrel ;
@ -327,29 +324,6 @@ make_rels_by_clauseless_joins(PlannerInfo *root,
}
}
/*
* is_inside_IN
* Detect whether the specified relation is inside an IN ( sub - SELECT ) .
*
* Note that we are actually only interested in rels that have been pulled up
* out of an IN , so the routine name is a slight misnomer .
*/
static bool
is_inside_IN ( PlannerInfo * root , RelOptInfo * rel )
{
ListCell * l ;
foreach ( l , root - > in_info_list )
{
InClauseInfo * ininfo = ( InClauseInfo * ) lfirst ( l ) ;
if ( bms_is_subset ( rel - > relids , ininfo - > righthand ) )
return true ;
}
return false ;
}
/*
/*
* make_jointree_rel
* make_jointree_rel
* Find or build a RelOptInfo join rel representing a specific
* Find or build a RelOptInfo join rel representing a specific
@ -603,3 +577,91 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
return joinrel ;
return joinrel ;
}
}
/*
* have_join_order_restriction
* Detect whether the two relations should be joined to satisfy
* a join - order restriction arising from IN clauses .
*
* 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 these tests because there are degenerate cases where
* a clauseless join must be performed to satisfy join - order restrictions .
*/
bool
have_join_order_restriction ( PlannerInfo * root ,
RelOptInfo * rel1 , RelOptInfo * rel2 )
{
ListCell * l ;
/*
* It ' s possible that the rels correspond to the left and right sides
* of a degenerate IN - clause ; in which case we should force the join
* to occur .
*
* Also , the two rels could represent a clauseless join that has to be
* completed to build up the LHS or RHS of an IN - clause .
*/
foreach ( l , root - > in_info_list )
{
InClauseInfo * ininfo = ( InClauseInfo * ) lfirst ( l ) ;
/* Can we perform the IN with these rels? */
if ( bms_is_subset ( ininfo - > lefthand , rel1 - > relids ) & &
bms_is_subset ( ininfo - > righthand , rel2 - > relids ) )
return true ;
if ( bms_is_subset ( ininfo - > lefthand , rel2 - > relids ) & &
bms_is_subset ( ininfo - > righthand , rel1 - > relids ) )
return true ;
/*
* Might we need to join these rels to complete the RHS ? It ' s
* probably overkill to test " overlap " , since we never join part of an
* IN ' s RHS to anything else , but may as well keep the coding similar
* to the OJ case .
*/
if ( bms_overlap ( ininfo - > righthand , rel1 - > relids ) & &
bms_overlap ( ininfo - > righthand , rel2 - > relids ) )
return true ;
/* Likewise for the LHS. */
if ( bms_overlap ( ininfo - > lefthand , rel1 - > relids ) & &
bms_overlap ( ininfo - > lefthand , rel2 - > relids ) )
return true ;
}
return false ;
}
/*
* has_join_restriction
* Detect whether the specified relation has join - order restrictions
* due to being inside an IN ( sub - SELECT ) .
*
* Essentially , this tests whether have_join_order_restriction ( ) could
* succeed with this rel and some other one .
*/
static bool
has_join_restriction ( PlannerInfo * root , RelOptInfo * rel )
{
ListCell * l ;
foreach ( l , root - > in_info_list )
{
InClauseInfo * ininfo = ( InClauseInfo * ) lfirst ( l ) ;
/* ignore if IN is already contained in rel */
if ( bms_is_subset ( ininfo - > lefthand , rel - > relids ) & &
bms_is_subset ( ininfo - > righthand , rel - > relids ) )
continue ;
/* restricted if it overlaps LHS or RHS, but doesn't contain IN */
if ( bms_overlap ( ininfo - > lefthand , rel - > relids ) | |
bms_overlap ( ininfo - > righthand , rel - > relids ) )
return true ;
}
return false ;
}