@ -58,7 +58,8 @@ typedef struct reduce_outer_joins_state
static Node * pull_up_sublinks_jointree_recurse ( PlannerInfo * root , Node * jtnode ,
static Node * pull_up_sublinks_jointree_recurse ( PlannerInfo * root , Node * jtnode ,
Relids * relids ) ;
Relids * relids ) ;
static Node * pull_up_sublinks_qual_recurse ( PlannerInfo * root , Node * node ,
static Node * pull_up_sublinks_qual_recurse ( PlannerInfo * root , Node * node ,
Relids available_rels , Node * * jtlink ) ;
Node * * jtlink1 , Relids available_rels1 ,
Node * * jtlink2 , Relids available_rels2 ) ;
static Node * pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode ,
static Node * pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode ,
RangeTblEntry * rte ,
RangeTblEntry * rte ,
JoinExpr * lowest_outer_join ,
JoinExpr * lowest_outer_join ,
@ -192,8 +193,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/* Set up a link representing the rebuilt jointree */
/* Set up a link representing the rebuilt jointree */
jtlink = ( Node * ) newf ;
jtlink = ( Node * ) newf ;
/* Now process qual --- all children are available for use */
/* Now process qual --- all children are available for use */
newf - > quals = pull_up_sublinks_qual_recurse ( root , f - > quals , frelids ,
newf - > quals = pull_up_sublinks_qual_recurse ( root , f - > quals ,
& jtlink ) ;
& jtlink , frelids ,
NULL , NULL ) ;
/*
/*
* Note that the result will be either newf , or a stack of JoinExprs
* Note that the result will be either newf , or a stack of JoinExprs
@ -237,16 +239,6 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
* point of the available_rels machinations is to ensure that we only
* point of the available_rels machinations is to ensure that we only
* pull up quals for which that ' s okay .
* pull up quals for which that ' s okay .
*
*
* XXX for the moment , we refrain from pulling up IN / EXISTS clauses
* appearing in LEFT or RIGHT join conditions . Although it is
* semantically valid to do so under the above conditions , we end up
* with a query in which the semijoin or antijoin must be evaluated
* below the outer join , which could perform far worse than leaving it
* as a sublink that is executed only for row pairs that meet the
* other join conditions . Fixing this seems to require considerable
* restructuring of the executor , but maybe someday it can happen .
* ( See also the comparable case in pull_up_sublinks_qual_recurse . )
*
* We don ' t expect to see any pre - existing JOIN_SEMI or JOIN_ANTI
* We don ' t expect to see any pre - existing JOIN_SEMI or JOIN_ANTI
* nodes here .
* nodes here .
*/
*/
@ -254,26 +246,25 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
{
{
case JOIN_INNER :
case JOIN_INNER :
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
& jtlink ,
bms_union ( leftrelids ,
bms_union ( leftrelids ,
rightrelids ) ,
rightrelids ) ,
& jtlink ) ;
NULL , NULL ) ;
break ;
break ;
case JOIN_LEFT :
case JOIN_LEFT :
# ifdef NOT_USED /* see XXX comment above */
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
& j - > rarg ,
rightrelids ,
rightrelids ,
& j - > rarg ) ;
NULL , NULL ) ;
# endif
break ;
break ;
case JOIN_FULL :
case JOIN_FULL :
/* can't do anything with full-join quals */
/* can't do anything with full-join quals */
break ;
break ;
case JOIN_RIGHT :
case JOIN_RIGHT :
# ifdef NOT_USED /* see XXX comment above */
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
j - > quals = pull_up_sublinks_qual_recurse ( root , j - > quals ,
& j - > larg ,
leftrelids ,
leftrelids ,
& j - > larg ) ;
NULL , NULL ) ;
# endif
break ;
break ;
default :
default :
elog ( ERROR , " unrecognized join type: %d " ,
elog ( ERROR , " unrecognized join type: %d " ,
@ -303,14 +294,22 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/*
/*
* Recurse through top - level qual nodes for pull_up_sublinks ( )
* Recurse through top - level qual nodes for pull_up_sublinks ( )
*
*
* jtlink points to the link in the jointree where any new JoinExprs should be
* jtlink1 points to the link in the jointree where any new JoinExprs should
* inserted . If we find multiple pull - up - able SubLinks , they ' ll get stacked
* be inserted if they reference available_rels1 ( i . e . , available_rels1
* there in the order we encounter them . We rely on subsequent optimization
* denotes the relations present underneath jtlink1 ) . Optionally , jtlink2 can
* to rearrange the stack if appropriate .
* point to a second link where new JoinExprs should be inserted if they
* reference available_rels2 ( pass NULL for both those arguments if not used ) .
* Note that SubLinks referencing both sets of variables cannot be optimized .
* If we find multiple pull - up - able SubLinks , they ' ll get stacked onto jtlink1
* and / or jtlink2 in the order we encounter them . We rely on subsequent
* optimization to rearrange the stack if appropriate .
*
* Returns the replacement qual node , or NULL if the qual should be removed .
*/
*/
static Node *
static Node *
pull_up_sublinks_qual_recurse ( PlannerInfo * root , Node * node ,
pull_up_sublinks_qual_recurse ( PlannerInfo * root , Node * node ,
Relids available_rels , Node * * jtlink )
Node * * jtlink1 , Relids available_rels1 ,
Node * * jtlink2 , Relids available_rels2 )
{
{
if ( node = = NULL )
if ( node = = NULL )
return NULL ;
return NULL ;
@ -323,45 +322,105 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
/* Is it a convertible ANY or EXISTS clause? */
/* Is it a convertible ANY or EXISTS clause? */
if ( sublink - > subLinkType = = ANY_SUBLINK )
if ( sublink - > subLinkType = = ANY_SUBLINK )
{
{
j = convert_ANY_sublink_to_join ( root , sublink ,
if ( ( j = convert_ANY_sublink_to_join ( root , sublink ,
available_rels ) ;
available_rels1 ) ) ! = NULL )
if ( j )
{
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink1 ;
* jtlink1 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
& child_rels ) ;
/*
* Now recursively process the pulled - up quals . Any inserted
* joins can get stacked onto either j - > larg or j - > rarg ,
* depending on which rels they reference .
*/
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals ,
& j - > larg ,
available_rels1 ,
& j - > rarg ,
child_rels ) ;
/* Return NULL representing constant TRUE */
return NULL ;
}
if ( available_rels2 ! = NULL & &
( j = convert_ANY_sublink_to_join ( root , sublink ,
available_rels2 ) ) ! = NULL )
{
{
/* Yes; recursively process what we pulled up */
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink2 ;
* jtlink2 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
j - > rarg ,
& child_rels ) ;
& child_rels ) ;
/* Any inserted joins get stacked onto j->rarg */
/*
* Now recursively process the pulled - up quals . Any inserted
* joins can get stacked onto either j - > larg or j - > rarg ,
* depending on which rels they reference .
*/
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals ,
j - > quals ,
child_rels ,
& j - > larg ,
& j - > rarg ) ;
available_rels2 ,
/* Now insert the new join node into the join tree */
& j - > rarg ,
j - > larg = * jtlink ;
child_rels ) ;
* jtlink = ( Node * ) j ;
/* Return NULL representing constant TRUE */
/* and return NULL representing constant TRUE */
return NULL ;
return NULL ;
}
}
}
}
else if ( sublink - > subLinkType = = EXISTS_SUBLINK )
else if ( sublink - > subLinkType = = EXISTS_SUBLINK )
{
{
j = convert_EXISTS_sublink_to_join ( root , sublink , false ,
if ( ( j = convert_EXISTS_sublink_to_join ( root , sublink , false ,
available_rels ) ;
available_rels1 ) ) ! = NULL )
if ( j )
{
{
/* Yes; recursively process what we pulled up */
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink1 ;
* jtlink1 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
j - > rarg ,
& child_rels ) ;
& child_rels ) ;
/* Any inserted joins get stacked onto j->rarg */
/*
* Now recursively process the pulled - up quals . Any inserted
* joins can get stacked onto either j - > larg or j - > rarg ,
* depending on which rels they reference .
*/
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals ,
j - > quals ,
child_rels ,
& j - > larg ,
& j - > rarg ) ;
available_rels1 ,
/* Now insert the new join node into the join tree */
& j - > rarg ,
j - > larg = * jtlink ;
child_rels ) ;
* jtlink = ( Node * ) j ;
/* Return NULL representing constant TRUE */
/* and return NULL representing constant TRUE */
return NULL ;
}
if ( available_rels2 ! = NULL & &
( j = convert_EXISTS_sublink_to_join ( root , sublink , false ,
available_rels2 ) ) ! = NULL )
{
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink2 ;
* jtlink2 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
& child_rels ) ;
/*
* Now recursively process the pulled - up quals . Any inserted
* joins can get stacked onto either j - > larg or j - > rarg ,
* depending on which rels they reference .
*/
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals ,
& j - > larg ,
available_rels2 ,
& j - > rarg ,
child_rels ) ;
/* Return NULL representing constant TRUE */
return NULL ;
return NULL ;
}
}
}
}
@ -373,40 +432,59 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
/* If the immediate argument of NOT is EXISTS, try to convert */
/* If the immediate argument of NOT is EXISTS, try to convert */
SubLink * sublink = ( SubLink * ) get_notclausearg ( ( Expr * ) node ) ;
SubLink * sublink = ( SubLink * ) get_notclausearg ( ( Expr * ) node ) ;
JoinExpr * j ;
JoinExpr * j ;
Relids child_rels ;
if ( sublink & & IsA ( sublink , SubLink ) )
if ( sublink & & IsA ( sublink , SubLink ) )
{
{
if ( sublink - > subLinkType = = EXISTS_SUBLINK )
if ( sublink - > subLinkType = = EXISTS_SUBLINK )
{
{
j = convert_EXISTS_sublink_to_join ( root , sublink , true ,
if ( ( j = convert_EXISTS_sublink_to_join ( root , sublink , true ,
available_rels ) ;
available_rels1 ) ) ! = NULL )
if ( j )
{
{
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink1 ;
* jtlink1 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
& child_rels ) ;
/*
/*
* For the moment , refrain from recursing underneath NOT .
* Now recursively process the pulled - up quals . Because
* As in pull_up_sublinks_jointree_recurse , recursing here
* we are underneath a NOT , we can ' t pull up sublinks
* would result in inserting a join underneath an ANTI
* that reference the left - hand stuff , but it ' s still
* join with which it could not commute , and that could
* okay to pull up sublinks referencing j - > rarg .
* easily lead to a worse plan than what we ' ve
* historically generated .
*/
*/
# ifdef NOT_USED
j - > quals = pull_up_sublinks_qual_recurse ( root ,
/* Yes; recursively process what we pulled up */
j - > quals ,
Relids child_rels ;
& j - > rarg ,
child_rels ,
NULL , NULL ) ;
/* Return NULL representing constant TRUE */
return NULL ;
}
if ( available_rels2 ! = NULL & &
( j = convert_EXISTS_sublink_to_join ( root , sublink , true ,
available_rels2 ) ) ! = NULL )
{
/* Yes; insert the new join node into the join tree */
j - > larg = * jtlink2 ;
* jtlink2 = ( Node * ) j ;
/* Recursively process pulled-up jointree nodes */
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg = pull_up_sublinks_jointree_recurse ( root ,
j - > rarg ,
j - > rarg ,
& child_rels ) ;
& child_rels ) ;
/* Any inserted joins get stacked onto j->rarg */
/*
* Now recursively process the pulled - up quals . Because
* we are underneath a NOT , we can ' t pull up sublinks
* that reference the left - hand stuff , but it ' s still
* okay to pull up sublinks referencing j - > rarg .
*/
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals = pull_up_sublinks_qual_recurse ( root ,
j - > quals ,
j - > quals ,
& j - > rarg ,
child_rels ,
child_rels ,
& j - > rarg ) ;
NULL , NULL ) ;
# endif
/* Return NULL representing constant TRUE */
/* Now insert the new join node into the join tree */
j - > larg = * jtlink ;
* jtlink = ( Node * ) j ;
/* and return NULL representing constant TRUE */
return NULL ;
return NULL ;
}
}
}
}
@ -427,8 +505,10 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
newclause = pull_up_sublinks_qual_recurse ( root ,
newclause = pull_up_sublinks_qual_recurse ( root ,
oldclause ,
oldclause ,
available_rels ,
jtlink1 ,
jtlink ) ;
available_rels1 ,
jtlink2 ,
available_rels2 ) ;
if ( newclause )
if ( newclause )
newclauses = lappend ( newclauses , newclause ) ;
newclauses = lappend ( newclauses , newclause ) ;
}
}