@ -120,7 +120,9 @@ static void reduce_outer_joins_pass2(Node *jtnode,
static Node * remove_useless_results_recurse ( PlannerInfo * root , Node * jtnode ) ;
static int get_result_relid ( PlannerInfo * root , Node * jtnode ) ;
static void remove_result_refs ( PlannerInfo * root , int varno , Node * newjtloc ) ;
static bool find_dependent_phvs ( Node * node , int varno ) ;
static bool find_dependent_phvs ( PlannerInfo * root , int varno ) ;
static bool find_dependent_phvs_in_jointree ( PlannerInfo * root ,
Node * node , int varno ) ;
static void substitute_phv_relids ( Node * node ,
int varno , Relids subrelids ) ;
static void fix_append_rel_relids ( List * append_rel_list , int varno ,
@ -2957,9 +2959,12 @@ reduce_outer_joins_pass2(Node *jtnode,
* and not remove the RTE_RESULT : there is noplace else to evaluate the
* PlaceHolderVar . ( That is , in such cases the RTE_RESULT * does * have output
* columns . ) But if the RTE_RESULT is an immediate child of an inner join ,
* we can change the PlaceHolderVar ' s phrels so as to evaluate it at the
* inner join instead . This is OK because we really only care that PHVs are
* evaluated above or below the correct outer joins .
* we can usually change the PlaceHolderVar ' s phrels so as to evaluate it at
* the inner join instead . This is OK because we really only care that PHVs
* are evaluated above or below the correct outer joins . We can ' t , however ,
* postpone the evaluation of a PHV to above where it is used ; so there are
* some checks below on whether output PHVs are laterally referenced in the
* other join input rel ( s ) .
*
* We used to try to do this work as part of pull_up_subqueries ( ) where the
* potentially - optimizable cases get introduced ; but it ' s way simpler , and
@ -3021,8 +3026,11 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
/*
* We can drop RTE_RESULT rels from the fromlist so long as at least
* one child remains , since joining to a one - row table changes
* nothing . The easiest way to mechanize this rule is to modify the
* list in - place .
* nothing . ( But we can ' t drop a RTE_RESULT that computes PHV ( s ) that
* are needed by some sibling . The cleanup transformation below would
* reassign the PHVs to be computed at the join , which is too late for
* the sibling ' s use . ) The easiest way to mechanize this rule is to
* modify the list in - place .
*/
foreach ( cell , f - > fromlist )
{
@ -3035,12 +3043,14 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
lfirst ( cell ) = child ;
/*
* If it ' s an RTE_RESULT with at least one sibling , we can drop
* it . We don ' t yet know what the inner join ' s final relid set
* will be , so postpone cleanup of PHVs etc till after this loop .
* If it ' s an RTE_RESULT with at least one sibling , and no sibling
* references dependent PHVs , we can drop it . We don ' t yet know
* what the inner join ' s final relid set will be , so postpone
* cleanup of PHVs etc till after this loop .
*/
if ( list_length ( f - > fromlist ) > 1 & &
( varno = get_result_relid ( root , child ) ) ! = 0 )
( varno = get_result_relid ( root , child ) ) ! = 0 & &
! find_dependent_phvs_in_jointree ( root , ( Node * ) f , varno ) )
{
f - > fromlist = foreach_delete_current ( f - > fromlist , cell ) ;
result_relids = bms_add_member ( result_relids , varno ) ;
@ -3091,8 +3101,18 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
* the join with a FromExpr with just the other side ; and if
* the qual is empty ( JOIN ON TRUE ) then we can omit the
* FromExpr as well .
*
* Just as in the FromExpr case , we can ' t simplify if the
* other input rel references any PHVs that are marked as to
* be evaluated at the RTE_RESULT rel , because we can ' t
* postpone their evaluation in that case . But we only have
* to check this in cases where it ' s syntactically legal for
* the other input to have a LATERAL reference to the
* RTE_RESULT rel . Only RHSes of inner and left joins are
* allowed to have such refs .
*/
if ( ( varno = get_result_relid ( root , j - > larg ) ) ! = 0 )
if ( ( varno = get_result_relid ( root , j - > larg ) ) ! = 0 & &
! find_dependent_phvs_in_jointree ( root , j - > rarg , varno ) )
{
remove_result_refs ( root , varno , j - > rarg ) ;
if ( j - > quals )
@ -3121,6 +3141,8 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
* strength - reduced to a plain inner join , since each LHS row
* necessarily has exactly one join partner . So we can always
* discard the RHS , much as in the JOIN_INNER case above .
* ( Again , the LHS could not contain a lateral reference to
* the RHS . )
*
* Otherwise , it ' s still true that each LHS row should be
* returned exactly once , and since the RHS returns no columns
@ -3131,7 +3153,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
*/
if ( ( varno = get_result_relid ( root , j - > rarg ) ) ! = 0 & &
( j - > quals = = NULL | |
! find_dependent_phvs ( ( Node * ) root - > parse , varno ) ) )
! find_dependent_phvs ( root , varno ) ) )
{
remove_result_refs ( root , varno , j - > larg ) ;
jtnode = j - > larg ;
@ -3141,7 +3163,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
/* Mirror-image of the JOIN_LEFT case */
if ( ( varno = get_result_relid ( root , j - > larg ) ) ! = 0 & &
( j - > quals = = NULL | |
! find_dependent_phvs ( ( Node * ) root - > parse , varno ) ) )
! find_dependent_phvs ( root , varno ) ) )
{
remove_result_refs ( root , varno , j - > rarg ) ;
jtnode = j - > rarg ;
@ -3162,7 +3184,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
*/
if ( ( varno = get_result_relid ( root , j - > rarg ) ) ! = 0 )
{
Assert ( ! find_dependent_phvs ( ( Node * ) root - > parse , varno ) ) ;
Assert ( ! find_dependent_phvs ( root , varno ) ) ;
remove_result_refs ( root , varno , j - > larg ) ;
if ( j - > quals )
jtnode = ( Node * )
@ -3246,6 +3268,12 @@ remove_result_refs(PlannerInfo *root, int varno, Node *newjtloc)
/*
* find_dependent_phvs - are there any PlaceHolderVars whose relids are
* exactly the given varno ?
*
* find_dependent_phvs should be used when we want to see if there are
* any such PHVs anywhere in the Query . Another use - case is to see if
* a subtree of the join tree contains such PHVs ; but for that , we have
* to look not only at the join tree nodes themselves but at the
* referenced RTEs . For that , use find_dependent_phvs_in_jointree .
*/
typedef struct
@ -3292,20 +3320,65 @@ find_dependent_phvs_walker(Node *node,
}
static bool
find_dependent_phvs ( Node * node , int varno )
find_dependent_phvs ( PlannerInfo * root , int varno )
{
find_dependent_phvs_context context ;
/* If there are no PHVs anywhere, we needn't work hard */
if ( root - > glob - > lastPHId = = 0 )
return false ;
context . relids = bms_make_singleton ( varno ) ;
context . sublevels_up = 0 ;
return query_tree_walker ( root - > parse ,
find_dependent_phvs_walker ,
( void * ) & context ,
0 ) ;
}
static bool
find_dependent_phvs_in_jointree ( PlannerInfo * root , Node * node , int varno )
{
find_dependent_phvs_context context ;
Relids subrelids ;
int relid ;
/* If there are no PHVs anywhere, we needn't work hard */
if ( root - > glob - > lastPHId = = 0 )
return false ;
context . relids = bms_make_singleton ( varno ) ;
context . sublevels_up = 0 ;
/*
* Must be prepared to start with a Query or a bare expression tree .
* See if the jointree fragment itself contains references ( in join quals )
*/
if ( find_dependent_phvs_walker ( node , & context ) )
return true ;
/*
* Otherwise , identify the set of referenced RTEs ( we can ignore joins ,
* since they should be flattened already , so their join alias lists no
* longer matter ) , and tediously check each RTE . We can ignore RTEs that
* are not marked LATERAL , though , since they couldn ' t possibly contain
* any cross - references to other RTEs .
*/
return query_or_expression_tree_walker ( node ,
find_dependent_phvs_walker ,
( void * ) & context ,
0 ) ;
subrelids = get_relids_in_jointree ( node , false ) ;
relid = - 1 ;
while ( ( relid = bms_next_member ( subrelids , relid ) ) > = 0 )
{
RangeTblEntry * rte = rt_fetch ( relid , root - > parse - > rtable ) ;
if ( rte - > lateral & &
range_table_entry_walker ( rte ,
find_dependent_phvs_walker ,
( void * ) & context ,
0 ) )
return true ;
}
return false ;
}
/*