@ -60,9 +60,14 @@ static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
static Node * pull_up_sublinks_qual_recurse ( PlannerInfo * root , Node * node ,
Node * * jtlink1 , Relids available_rels1 ,
Node * * jtlink2 , Relids available_rels2 ) ;
static Node * pull_up_subqueries_recurse ( PlannerInfo * root , Node * jtnode ,
JoinExpr * lowest_outer_join ,
JoinExpr * lowest_nulling_outer_join ,
AppendRelInfo * containing_appendrel ) ;
static Node * pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode ,
RangeTblEntry * rte ,
JoinExpr * lowest_outer_join ,
JoinExpr * lowest_nulling_outer_join ,
AppendRelInfo * containing_appendrel ) ;
static Node * pull_up_simple_union_all ( PlannerInfo * root , Node * jtnode ,
RangeTblEntry * rte ) ;
@ -71,14 +76,15 @@ static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
int childRToffset ) ;
static void make_setop_translation_list ( Query * query , Index newvarno ,
List * * translated_vars ) ;
static bool is_simple_subquery ( Query * subquery ) ;
static bool is_simple_subquery ( Query * subquery , RangeTblEntry * rte ,
JoinExpr * lowest_outer_join ) ;
static bool is_simple_union_all ( Query * subquery ) ;
static bool is_simple_union_all_recurse ( Node * setOp , Query * setOpQuery ,
List * colTypes ) ;
static bool is_safe_append_member ( Query * subquery ) ;
static void replace_vars_in_jointree ( Node * jtnode ,
pullup_replace_vars_context * context ,
JoinExpr * lowest_outer_join ) ;
JoinExpr * lowest_nulling_ outer_join ) ;
static Node * pullup_replace_vars ( Node * expr ,
pullup_replace_vars_context * context ) ;
static Node * pullup_replace_vars_callback ( Var * var ,
@ -585,10 +591,28 @@ inline_set_returning_functions(PlannerInfo *root)
* Also , subqueries that are simple UNION ALL structures can be
* converted into " append relations " .
*
* This recursively processes the jointree and returns a modified jointree .
*/
Node *
pull_up_subqueries ( PlannerInfo * root , Node * jtnode )
{
/* Start off with no containing join nor appendrel */
return pull_up_subqueries_recurse ( root , jtnode , NULL , NULL , NULL ) ;
}
/*
* pull_up_subqueries_recurse
* Recursive guts of pull_up_subqueries .
*
* If this jointree node is within either side of an outer join , then
* lowest_outer_join references the lowest such JoinExpr node ; otherwise
* it is NULL . We use this to constrain the effects of LATERAL subqueries .
*
* 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 .
* lowest_nulling_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 .
*
* If we are looking at a member subquery of an append relation ,
* containing_appendrel describes that relation ; else it is NULL .
@ -603,14 +627,16 @@ inline_set_returning_functions(PlannerInfo *root)
* subquery RangeTblRef entries will be replaced . Also , we can ' t turn
* pullup_replace_vars 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 .
* This behavior is what makes it reasonable to pass lowest_outer_join and
* lowest_nulling_outer_join as pointers rather than some more - indirect way
* of identifying the lowest OJs . 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 ,
static Node *
pull_up_subqueries_recurse ( PlannerInfo * root , Node * jtnode ,
JoinExpr * lowest_outer_join ,
JoinExpr * lowest_nulling_outer_join ,
AppendRelInfo * containing_appendrel )
{
if ( jtnode = = NULL )
@ -628,12 +654,12 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
* unless is_safe_append_member says so .
*/
if ( rte - > rtekind = = RTE_SUBQUERY & &
is_simple_subquery ( rte - > subquery ) & &
! rte - > security_barrier & &
is_simple_subquery ( rte - > subquery , rte , lowest_outer_join ) & &
( containing_appendrel = = NULL | |
is_safe_append_member ( rte - > subquery ) ) )
return pull_up_simple_subquery ( root , jtnode , rte ,
lowest_outer_join ,
lowest_nulling_outer_join ,
containing_appendrel ) ;
/*
@ -658,8 +684,10 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
Assert ( containing_appendrel = = NULL ) ;
foreach ( l , f - > fromlist )
lfirst ( l ) = pull_up_subqueries ( root , lfirst ( l ) ,
lowest_outer_join , NULL ) ;
lfirst ( l ) = pull_up_subqueries_recurse ( root , lfirst ( l ) ,
lowest_outer_join ,
lowest_nulling_outer_join ,
NULL ) ;
}
else if ( IsA ( jtnode , JoinExpr ) )
{
@ -670,30 +698,46 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
switch ( j - > jointype )
{
case JOIN_INNER :
j - > larg = pull_up_subqueries ( root , j - > larg ,
lowest_outer_join , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
lowest_outer_join , NULL ) ;
j - > larg = pull_up_subqueries_recurse ( root , j - > larg ,
lowest_outer_join ,
lowest_nulling_outer_join ,
NULL ) ;
j - > rarg = pull_up_subqueries_recurse ( root , j - > rarg ,
lowest_outer_join ,
lowest_nulling_outer_join ,
NULL ) ;
break ;
case JOIN_LEFT :
case JOIN_SEMI :
case JOIN_ANTI :
j - > larg = pull_up_subqueries ( root , j - > larg ,
lowest_outer_join , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
j , NULL ) ;
j - > larg = pull_up_subqueries_recurse ( root , j - > larg ,
j ,
lowest_nulling_outer_join ,
NULL ) ;
j - > rarg = pull_up_subqueries_recurse ( root , j - > rarg ,
j ,
j ,
NULL ) ;
break ;
case JOIN_FULL :
j - > larg = pull_up_subqueries ( root , j - > larg ,
j , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
j , NULL ) ;
j - > larg = pull_up_subqueries_recurse ( root , j - > larg ,
j ,
j ,
NULL ) ;
j - > rarg = pull_up_subqueries_recurse ( root , j - > rarg ,
j ,
j ,
NULL ) ;
break ;
case JOIN_RIGHT :
j - > larg = pull_up_subqueries ( root , j - > larg ,
j , NULL ) ;
j - > rarg = pull_up_subqueries ( root , j - > rarg ,
lowest_outer_join , NULL ) ;
j - > larg = pull_up_subqueries_recurse ( root , j - > larg ,
j ,
j ,
NULL ) ;
j - > rarg = pull_up_subqueries_recurse ( root , j - > rarg ,
j ,
lowest_nulling_outer_join ,
NULL ) ;
break ;
default :
elog ( ERROR , " unrecognized join type: %d " ,
@ -717,11 +761,12 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
* all .
*
* rte is the RangeTblEntry referenced by jtnode . Remaining parameters are
* as for pull_up_subqueries .
* as for pull_up_subqueries_recurse .
*/
static Node *
pull_up_simple_subquery ( PlannerInfo * root , Node * jtnode , RangeTblEntry * rte ,
JoinExpr * lowest_outer_join ,
JoinExpr * lowest_nulling_outer_join ,
AppendRelInfo * containing_appendrel )
{
Query * parse = root - > parse ;
@ -788,7 +833,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* handling an appendrel member .
*/
subquery - > jointree = ( FromExpr * )
pull_up_subqueries ( subroot , ( Node * ) subquery - > jointree , NULL , NULL ) ;
pull_up_subqueries_recurse ( subroot , ( Node * ) subquery - > jointree ,
NULL , NULL , NULL ) ;
/*
* Now we must recheck whether the subquery is still simple enough to pull
@ -796,10 +842,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
*
* We don ' t really need to recheck all the conditions involved , but it ' s
* easier just to keep this " if " looking the same as the one in
* pull_up_subqueries .
* pull_up_subqueries_recurse .
*/
if ( is_simple_subquery ( subquery ) & &
! rte - > security_barrier & &
if ( is_simple_subquery ( subquery , rte , lowest_outer_join ) & &
( containing_appendrel = = NULL | | is_safe_append_member ( subquery ) ) )
{
/* good to go */
@ -846,7 +891,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
rvcontext . target_rte = rte ;
rvcontext . outer_hasSubLinks = & parse - > hasSubLinks ;
rvcontext . varno = varno ;
rvcontext . need_phvs = ( lowest_outer_join ! = NULL | |
rvcontext . need_phvs = ( lowest_nulling_ outer_join ! = NULL | |
containing_appendrel ! = NULL ) ;
rvcontext . wrap_non_vars = ( containing_appendrel ! = NULL ) ;
/* initialize cache array with indexes 0 .. length(tlist) */
@ -867,7 +912,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
parse - > returningList = ( List * )
pullup_replace_vars ( ( Node * ) parse - > returningList , & rvcontext ) ;
replace_vars_in_jointree ( ( Node * ) parse - > jointree , & rvcontext ,
lowest_outer_join ) ;
lowest_nulling_ outer_join ) ;
Assert ( parse - > setOperations = = NULL ) ;
parse - > havingQual = pullup_replace_vars ( parse - > havingQual , & rvcontext ) ;
@ -894,10 +939,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* 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 . )
* below lowest_nulling_ 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 )
{
@ -909,6 +954,38 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
& rvcontext ) ;
}
/*
* If the subquery had a LATERAL marker , propagate that to any of its
* child RTEs that could possibly now contain lateral cross - references .
* The children might or might not contain any actual lateral
* cross - references , but we have to mark the pulled - up child RTEs so that
* later planner stages will check for such .
*
* NB : although the parser only sets the lateral flag in subquery and
* function RTEs , after this step it can also be set in VALUES RTEs .
*/
if ( rte - > lateral )
{
foreach ( lc , subquery - > rtable )
{
RangeTblEntry * child_rte = ( RangeTblEntry * ) lfirst ( lc ) ;
switch ( child_rte - > rtekind )
{
case RTE_SUBQUERY :
case RTE_FUNCTION :
case RTE_VALUES :
child_rte - > lateral = true ;
break ;
case RTE_RELATION :
case RTE_JOIN :
case RTE_CTE :
/* these can't contain any lateral references */
break ;
}
}
}
/*
* Now append the adjusted rtable entries to upper query . ( We hold off
* until after fixing the upper rtable entries ; no point in running that
@ -1104,11 +1181,12 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
* must build the AppendRelInfo first , because this will modify it . )
* 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 .
* aren ' t going to propagate up to the join .
*/
rtr = makeNode ( RangeTblRef ) ;
rtr - > rtindex = childRTindex ;
( void ) pull_up_subqueries ( root , ( Node * ) rtr , NULL , appinfo ) ;
( void ) pull_up_subqueries_recurse ( root , ( Node * ) rtr ,
NULL , NULL , appinfo ) ;
}
else if ( IsA ( setOp , SetOperationStmt ) )
{
@ -1158,9 +1236,15 @@ make_setop_translation_list(Query *query, Index newvarno,
* is_simple_subquery
* Check a subquery in the range table to see if it ' s simple enough
* to pull up into the parent query .
*
* rte is the RTE_SUBQUERY RangeTblEntry that contained the subquery .
* ( Note subquery is not necessarily equal to rte - > subquery ; it could be a
* processed copy of that . )
* lowest_outer_join is the lowest outer join above the subquery , or NULL .
*/
static bool
is_simple_subquery ( Query * subquery )
is_simple_subquery ( Query * subquery , RangeTblEntry * rte ,
JoinExpr * lowest_outer_join )
{
/*
* Let ' s just make sure it ' s a valid subselect . . .
@ -1201,11 +1285,29 @@ is_simple_subquery(Query *subquery)
return false ;
/*
* Don ' t pull up a LATERAL subquery ( hopefully , this is just a temporary
* implementation restriction ) .
* Don ' t pull up if the RTE represents a security - barrier view ; we couldn ' t
* prevent information leakage once the RTE ' s Vars are scattered about in
* the upper query .
*/
if ( rte - > security_barrier )
return false ;
/*
* If the subquery is LATERAL , and we ' re below any outer join , and the
* subquery contains lateral references to rels outside the outer join ,
* don ' t pull up . Doing so would risk creating outer - join quals that
* contain references to rels outside the outer join , which is a semantic
* mess that doesn ' t seem worth addressing at the moment .
*/
if ( contain_vars_of_level ( ( Node * ) subquery , 1 ) )
if ( rte - > lateral & & lowest_outer_join ! = NULL )
{
Relids lvarnos = pull_varnos_of_level ( ( Node * ) subquery , 1 ) ;
Relids jvarnos = get_relids_in_jointree ( ( Node * ) lowest_outer_join ,
true ) ;
if ( ! bms_is_subset ( lvarnos , jvarnos ) )
return false ;
}
/*
* Don ' t pull up a subquery that has any set - returning functions in its
@ -1358,13 +1460,13 @@ is_safe_append_member(Query *subquery)
* expression in the jointree , without changing the jointree structure itself .
* Ugly , but there ' s no other way . . .
*
* If we are at or below lowest_outer_join , we can suppress use of
* If we are at or below lowest_nulling_ outer_join , we can suppress use of
* PlaceHolderVars wrapped around the replacement expressions .
*/
static void
replace_vars_in_jointree ( Node * jtnode ,
pullup_replace_vars_context * context ,
JoinExpr * lowest_outer_join )
JoinExpr * lowest_nulling_ outer_join )
{
if ( jtnode = = NULL )
return ;
@ -1378,7 +1480,8 @@ replace_vars_in_jointree(Node *jtnode,
ListCell * l ;
foreach ( l , f - > fromlist )
replace_vars_in_jointree ( lfirst ( l ) , context , lowest_outer_join ) ;
replace_vars_in_jointree ( lfirst ( l ) , context ,
lowest_nulling_outer_join ) ;
f - > quals = pullup_replace_vars ( f - > quals , context ) ;
}
else if ( IsA ( jtnode , JoinExpr ) )
@ -1386,14 +1489,14 @@ replace_vars_in_jointree(Node *jtnode,
JoinExpr * j = ( JoinExpr * ) jtnode ;
bool save_need_phvs = context - > need_phvs ;
if ( j = = lowest_outer_join )
if ( j = = lowest_nulling_ outer_join )
{
/* no more PHVs in or below this join */
context - > need_phvs = false ;
lowest_outer_join = NULL ;
lowest_nulling_ outer_join = NULL ;
}
replace_vars_in_jointree ( j - > larg , context , lowest_outer_join ) ;
replace_vars_in_jointree ( j - > rarg , context , lowest_outer_join ) ;
replace_vars_in_jointree ( j - > larg , context , lowest_nulling_ outer_join ) ;
replace_vars_in_jointree ( j - > rarg , context , lowest_nulling_ outer_join ) ;
j - > quals = pullup_replace_vars ( j - > quals , context ) ;
/*