@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / optimizer / prep / prepjointree . c , v 1.64 2009 / 02 / 27 23 : 30 : 29 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / optimizer / prep / prepjointree . c , v 1.65 2009 / 04 / 28 21 : 31 : 16 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -47,8 +47,8 @@ static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
Relids available_rels , Node * * jtlink ) ;
static Node * pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode ,
RangeTblEntry * rte ,
bool below _outer_join,
bool append_rel_member ) ;
JoinExpr * lowest _outer_join,
AppendRelInfo * containing_appendrel ) ;
static Node * pull_up_simple_union_all ( PlannerInfo * root , Node * jtnode ,
RangeTblEntry * rte ) ;
static void pull_up_union_leaf_queries ( Node * setOp , PlannerInfo * root ,
@ -63,8 +63,9 @@ static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
static List * insert_targetlist_placeholders ( PlannerInfo * root , List * tlist ,
int varno , bool wrap_non_vars ) ;
static bool is_safe_append_member ( Query * subquery ) ;
static void resolvenew_in_jointree ( Node * jtnode , int varno ,
RangeTblEntry * rte , List * subtlist ) ;
static void resolvenew_in_jointree ( Node * jtnode , int varno , RangeTblEntry * rte ,
List * subtlist , List * subtlist_with_phvs ,
JoinExpr * lowest_outer_join ) ;
static reduce_outer_joins_state * reduce_outer_joins_pass1 ( Node * jtnode ) ;
static void reduce_outer_joins_pass2 ( Node * jtnode ,
reduce_outer_joins_state * state ,
@ -440,14 +441,15 @@ inline_set_returning_functions(PlannerInfo *root)
* Also , subqueries that are simple UNION ALL structures can be
* converted into " append relations " .
*
* below_outer_join is true if this jointree node is within the nullable
* side of an outer join . This forces use of the PlaceHolderVar mechanism
* for non - nullable targetlist items .
* If this jointree node is within the nullable side of an outer join , then
* lowest_outer_join references the lowest such JoinExpr node ; otherwise it
* is NULL . This forces use of the PlaceHolderVar mechanism for references
* to non - nullable targetlist items , but only for references above that join .
*
* append_rel_member is true i f we are looking at a member subquery of
* an append relation . This forces use of the PlaceHolderVar mechanism
* for all non - Var targetlist items , and puts some additional restrictions
* on what can be pulled up .
* I f we are looking at a member subquery of an append relation ,
* containing_appendrel describes that relation ; else it is NULL .
* This forces use of the PlaceHolderVar mechanism for all non - Var targetlist
* items , and puts some additional restrictions on what can be pulled up .
*
* A tricky aspect of this code is that if we pull up a subquery we have
* to replace Vars that reference the subquery ' s outputs throughout the
@ -457,10 +459,15 @@ inline_set_returning_functions(PlannerInfo *root)
* subquery RangeTblRef entries will be replaced . Also , we can ' t turn
* ResolveNew loose on the whole jointree , because it ' ll return a mutated
* copy of the tree ; we have to invoke it just on the quals , instead .
* This behavior is what makes it reasonable to pass lowest_outer_join as a
* pointer rather than some more - indirect way of identifying the lowest OJ .
* Likewise , we don ' t replace append_rel_list members but only their
* substructure , so the containing_appendrel reference is safe to use .
*/
Node *
pull_up_subqueries ( PlannerInfo * root , Node * jtnode ,
bool below_outer_join , bool append_rel_member )
JoinExpr * lowest_outer_join ,
AppendRelInfo * containing_appendrel )
{
if ( jtnode = = NULL )
return NULL ;
@ -478,10 +485,11 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
*/
if ( rte - > rtekind = = RTE_SUBQUERY & &
is_simple_subquery ( rte - > subquery ) & &
( ! append_rel_member | | is_safe_append_member ( rte - > subquery ) ) )
( containing_appendrel = = NULL | |
is_safe_append_member ( rte - > subquery ) ) )
return pull_up_simple_subquery ( root , jtnode , rte ,
be low_outer_join,
append_ rel_member ) ;
lowest _outer_join ,
containing_ appendrel) ;
/*
* Alternatively , is it a simple UNION ALL subquery ? If so , flatten
@ -503,44 +511,44 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
FromExpr * f = ( FromExpr * ) jtnode ;
ListCell * l ;
Assert ( ! append_rel_member ) ;
Assert ( containing_appendrel = = NULL ) ;
foreach ( l , f - > fromlist )
lfirst ( l ) = pull_up_subqueries ( root , lfirst ( l ) ,
be low_outer_join, false ) ;
lowest _outer_join , NULL ) ;
}
else if ( IsA ( jtnode , JoinExpr ) )
{
JoinExpr * j = ( JoinExpr * ) jtnode ;
Assert ( ! append_rel_member ) ;
Assert ( containing_appendrel = = NULL ) ;
/* Recurse, being careful to tell myself when inside outer join */
switch ( j - > jointype )
{
case JOIN_INNER :
j - > larg = pull_up_subqueries ( root , j - > larg ,
be low_outer_join, false ) ;
lowest _outer_join , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
be low_outer_join, false ) ;
lowest _outer_join , NULL ) ;
break ;
case JOIN_LEFT :
case JOIN_SEMI :
case JOIN_ANTI :
j - > larg = pull_up_subqueries ( root , j - > larg ,
be low_outer_join, false ) ;
lowest _outer_join , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
true , false ) ;
j , NULL ) ;
break ;
case JOIN_FULL :
j - > larg = pull_up_subqueries ( root , j - > larg ,
true , false ) ;
j , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
true , false ) ;
j , NULL ) ;
break ;
case JOIN_RIGHT :
j - > larg = pull_up_subqueries ( root , j - > larg ,
true , false ) ;
j , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
be low_outer_join, false ) ;
lowest _outer_join , NULL ) ;
break ;
default :
elog ( ERROR , " unrecognized join type: %d " ,
@ -562,10 +570,14 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
* subquery by pull_up_subqueries . We return the replacement jointree node ,
* or jtnode itself if we determine that the subquery can ' t be pulled up after
* all .
*
* rte is the RangeTblEntry referenced by jtnode . Remaining parameters are
* as for pull_up_subqueries .
*/
static Node *
pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode , RangeTblEntry * rte ,
bool below_outer_join , bool append_rel_member )
JoinExpr * lowest_outer_join ,
AppendRelInfo * containing_appendrel )
{
Query * parse = root - > parse ;
int varno = ( ( RangeTblRef * ) jtnode ) - > rtindex ;
@ -573,7 +585,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
PlannerInfo * subroot ;
int rtoffset ;
List * subtlist ;
ListCell * rt ;
List * subtlist_with_phvs ;
ListCell * lc ;
/*
* Need a modifiable copy of the subquery to hack on . Even if we didn ' t
@ -624,13 +637,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* pull_up_subqueries ' processing is complete for its jointree and
* rangetable .
*
* Note : below_outer_join = false is correct here even if we are within a n
* outer join in the upper query ; the lower query starts with a clean
* Note : we should pass NULL for containing - join info even if we are within
* an outer join in the upper query ; the lower query starts with a clean
* slate for outer - join semantics . Likewise , we say we aren ' t handling an
* appendrel member .
*/
subquery - > jointree = ( FromExpr * )
pull_up_subqueries ( subroot , ( Node * ) subquery - > jointree , false , false ) ;
pull_up_subqueries ( subroot , ( Node * ) subquery - > jointree , NULL , NULL ) ;
/*
* Now we must recheck whether the subquery is still simple enough to pull
@ -641,7 +654,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* pull_up_subqueries .
*/
if ( is_simple_subquery ( subquery ) & &
( ! append_rel_member | | is_safe_append_member ( subquery ) ) )
( containing_appendrel = = NULL | | is_safe_append_member ( subquery ) ) )
{
/* good to go */
}
@ -677,51 +690,82 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
/*
* The subquery ' s targetlist items are now in the appropriate form to
* insert into the top query , but if we are under an outer join then
* non - nullable items have to be turned into PlaceHolderVars . If we
* non - nullable items may have to be turned into PlaceHolderVars . If we
* are dealing with an appendrel member then anything that ' s not a
* simple Var has to be turned into a PlaceHolderVar .
*/
if ( below_outer_join | | append_rel_member )
subtlist = insert_targetlist_placeholders ( root , subquery - > targetList ,
varno , append_rel_member ) ;
else
subtlist = subquery - > targetList ;
if ( lowest_outer_join ! = NULL | | containing_appendrel ! = NULL )
subtlist_with_phvs = insert_targetlist_placeholders ( root ,
subtlist ,
varno ,
containing_appendrel ! = NULL ) ;
else
subtlist_with_phvs = subtlist ;
/*
* Replace all of the top query ' s references to the subquery ' s outputs
* with copies of the adjusted subtlist items , being careful not to
* replace any of the jointree structure . ( This ' d be a lot cleaner if we
* could use query_tree_mutator . )
* could use query_tree_mutator . ) We have to use PHVs in the targetList ,
* returningList , and havingQual , since those are certainly above any
* outer join . resolvenew_in_jointree tracks its location in the jointree
* and uses PHVs or not appropriately .
*/
parse - > targetList = ( List * )
ResolveNew ( ( Node * ) parse - > targetList ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
subtlist_with_phvs , CMD_SELECT , 0 ) ;
parse - > returningList = ( List * )
ResolveNew ( ( Node * ) parse - > returningList ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
resolvenew_in_jointree ( ( Node * ) parse - > jointree , varno ,
rte , subtlist ) ;
subtlist_with_phvs , CMD_SELECT , 0 ) ;
resolvenew_in_jointree ( ( Node * ) parse - > jointree , varno , rte ,
subtlist , subtlist_with_phvs ,
lowest_outer_join ) ;
Assert ( parse - > setOperations = = NULL ) ;
parse - > havingQual =
ResolveNew ( parse - > havingQual ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
root - > append_rel_list = ( List * )
ResolveNew ( ( Node * ) root - > append_rel_list ,
subtlist_with_phvs , CMD_SELECT , 0 ) ;
/*
* Replace references in the translated_vars lists of appendrels .
* When pulling up an appendrel member , we do not need PHVs in the list
* of the parent appendrel - - - there isn ' t any outer join between .
* Elsewhere , use PHVs for safety . ( This analysis could be made tighter
* but it seems unlikely to be worth much trouble . )
*/
foreach ( lc , root - > append_rel_list )
{
AppendRelInfo * appinfo = ( AppendRelInfo * ) lfirst ( lc ) ;
appinfo - > translated_vars = ( List * )
ResolveNew ( ( Node * ) appinfo - > translated_vars ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
( appinfo = = containing_appendrel ) ?
subtlist : subtlist_with_phvs ,
CMD_SELECT , 0 ) ;
}
foreach ( rt , parse - > rtable )
/*
* Replace references in the joinaliasvars lists of join RTEs .
*
* You might think that we could avoid using PHVs for alias vars of joins
* below lowest_outer_join , but that doesn ' t work because the alias vars
* could be referenced above that join ; we need the PHVs to be present
* in such references after the alias vars get flattened . ( It might be
* worth trying to be smarter here , someday . )
*/
foreach ( lc , parse - > rtable )
{
RangeTblEntry * otherrte = ( RangeTblEntry * ) lfirst ( rt ) ;
RangeTblEntry * otherrte = ( RangeTblEntry * ) lfirst ( lc ) ;
if ( otherrte - > rtekind = = RTE_JOIN )
otherrte - > joinaliasvars = ( List * )
ResolveNew ( ( Node * ) otherrte - > joinaliasvars ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
subtlist_with_phvs , CMD_SELECT , 0 ) ;
}
/*
@ -884,13 +928,13 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
/*
* Recursively apply pull_up_subqueries to the new child RTE . ( We
* must build the AppendRelInfo first , because this will modify it . )
* Note that we can pass below_outer_join = false even if we ' re
* Note that we can pass NULL for containing - join info even if we ' re
* actually under an outer join , because the child ' s expressions
* aren ' t going to propagate up above the join .
*/
rtr = makeNode ( RangeTblRef ) ;
rtr - > rtindex = childRTindex ;
( void ) pull_up_subqueries ( root , ( Node * ) rtr , false , true ) ;
( void ) pull_up_subqueries ( root , ( Node * ) rtr , NULL , appinfo ) ;
}
else if ( IsA ( setOp , SetOperationStmt ) )
{
@ -1096,7 +1140,7 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
* Insert PlaceHolderVar nodes into any non - junk targetlist items that are
* not simple variables or strict functions of simple variables ( and hence
* might not correctly go to NULL when examined above the point of an outer
* join ) . We assume we can modify the tlist items in - place .
* join ) .
*
* varno is the upper - query relid of the subquery ; this is used as the
* syntactic location of the PlaceHolderVars .
@ -1107,15 +1151,20 @@ static List *
insert_targetlist_placeholders ( PlannerInfo * root , List * tlist ,
int varno , bool wrap_non_vars )
{
List * result = NIL ;
ListCell * lc ;
foreach ( lc , tlist )
{
TargetEntry * tle = ( TargetEntry * ) lfirst ( lc ) ;
TargetEntry * newtle ;
/* ignore resjunk columns */
/* resjunk columns need not be changed */
if ( tle - > resjunk )
{
result = lappend ( result , tle ) ;
continue ;
}
/*
* Simple Vars always escape being wrapped . This is common enough
@ -1123,7 +1172,10 @@ insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
*/
if ( tle - > expr & & IsA ( tle - > expr , Var ) & &
( ( Var * ) tle - > expr ) - > varlevelsup = = 0 )
{
result = lappend ( result , tle ) ;
continue ;
}
if ( ! wrap_non_vars )
{
@ -1136,15 +1188,22 @@ insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
*/
if ( contain_vars_of_level ( ( Node * ) tle - > expr , 0 ) & &
! contain_nonstrict_functions ( ( Node * ) tle - > expr ) )
{
result = lappend ( result , tle ) ;
continue ;
}
}
/* Else wrap it in a PlaceHolderVar */
tle - > expr = ( Expr * ) make_placeholder_expr ( root ,
newtle = makeNode ( TargetEntry ) ;
memcpy ( newtle , tle , sizeof ( TargetEntry ) ) ;
newtle - > expr = ( Expr * )
make_placeholder_expr ( root ,
tle - > expr ,
bms_make_singleton ( varno ) ) ;
result = lappend ( result , newtle ) ;
}
return tlis t;
return resul t;
}
/*
@ -1187,10 +1246,15 @@ is_safe_append_member(Query *subquery)
* Helper routine for pull_up_subqueries : do ResolveNew on every expression
* in the jointree , without changing the jointree structure itself . Ugly ,
* but there ' s no other way . . .
*
* If we are above lowest_outer_join then use subtlist_with_phvs ; at or
* below it , use subtlist . ( When no outer joins are in the picture ,
* these will be the same list . )
*/
static void
resolvenew_in_jointree ( Node * jtnode , int varno ,
RangeTblEntry * rte , List * subtlist )
resolvenew_in_jointree ( Node * jtnode , int varno , RangeTblEntry * rte ,
List * subtlist , List * subtlist_with_phvs ,
JoinExpr * lowest_outer_join )
{
if ( jtnode = = NULL )
return ;
@ -1204,20 +1268,32 @@ resolvenew_in_jointree(Node *jtnode, int varno,
ListCell * l ;
foreach ( l , f - > fromlist )
resolvenew_in_jointree ( lfirst ( l ) , varno , rte , subtlist ) ;
resolvenew_in_jointree ( lfirst ( l ) , varno , rte ,
subtlist , subtlist_with_phvs ,
lowest_outer_join ) ;
f - > quals = ResolveNew ( f - > quals ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
subtlist_with_phvs , CMD_SELECT , 0 ) ;
}
else if ( IsA ( jtnode , JoinExpr ) )
{
JoinExpr * j = ( JoinExpr * ) jtnode ;
resolvenew_in_jointree ( j - > larg , varno , rte , subtlist ) ;
resolvenew_in_jointree ( j - > rarg , varno , rte , subtlist ) ;
if ( j = = lowest_outer_join )
{
/* no more PHVs in or below this join */
subtlist_with_phvs = subtlist ;
lowest_outer_join = NULL ;
}
resolvenew_in_jointree ( j - > larg , varno , rte ,
subtlist , subtlist_with_phvs ,
lowest_outer_join ) ;
resolvenew_in_jointree ( j - > rarg , varno , rte ,
subtlist , subtlist_with_phvs ,
lowest_outer_join ) ;
j - > quals = ResolveNew ( j - > quals ,
varno , 0 , rte ,
subtlist , CMD_SELECT , 0 ) ;
subtlist_with_phvs , CMD_SELECT , 0 ) ;
/*
* We don ' t bother to update the colvars list , since it won ' t be used