@ -5363,7 +5363,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
* Handle a simple Var for examine_variable
*
* This is split out as a subroutine so that we can recurse to deal with
* Vars referencing subqueries .
* Vars referencing subqueries ( either sub - SELECT - in - FROM or CTE style ) .
*
* We already filled in all the fields of * vardata except for the stats tuple .
*/
@ -5497,13 +5497,19 @@ examine_simple_variable(PlannerInfo *root, Var *var,
vardata - > acl_ok = true ;
}
}
else if ( rte - > rtekind = = RTE_SUBQUERY & & ! rte - > inh )
else if ( ( rte - > rtekind = = RTE_SUBQUERY & & ! rte - > inh ) | |
( rte - > rtekind = = RTE_CTE & & ! rte - > self_reference ) )
{
/*
* Plain subquery ( not one that was converted to an appendrel ) .
* Plain subquery ( not one that was converted to an appendrel ) or
* non - recursive CTE . In either case , we can try to find out what the
* Var refers to within the subquery . We skip this for appendrel and
* recursive - CTE cases because any column stats we did find would
* likely not be very relevant .
*/
Query * subquery = rte - > subquery ;
RelOptInfo * rel ;
PlannerInfo * subroot ;
Query * subquery ;
List * subtlist ;
TargetEntry * ste ;
/*
@ -5512,6 +5518,85 @@ examine_simple_variable(PlannerInfo *root, Var *var,
if ( var - > varattno = = InvalidAttrNumber )
return ;
/*
* Otherwise , find the subquery ' s planner subroot .
*/
if ( rte - > rtekind = = RTE_SUBQUERY )
{
RelOptInfo * rel ;
/*
* Fetch RelOptInfo for subquery . Note that we don ' t change the
* rel returned in vardata , since caller expects it to be a rel of
* the caller ' s query level . Because we might already be
* recursing , we can ' t use that rel pointer either , but have to
* look up the Var ' s rel afresh .
*/
rel = find_base_rel ( root , var - > varno ) ;
subroot = rel - > subroot ;
}
else
{
/* CTE case is more difficult */
PlannerInfo * cteroot ;
Index levelsup ;
int ndx ;
int plan_id ;
ListCell * lc ;
/*
* Find the referenced CTE , and locate the subroot previously made
* for it .
*/
levelsup = rte - > ctelevelsup ;
cteroot = root ;
while ( levelsup - - > 0 )
{
cteroot = cteroot - > parent_root ;
if ( ! cteroot ) /* shouldn't happen */
elog ( ERROR , " bad levelsup for CTE \" %s \" " , rte - > ctename ) ;
}
/*
* Note : cte_plan_ids can be shorter than cteList , if we are still
* working on planning the CTEs ( ie , this is a side - reference from
* another CTE ) . So we mustn ' t use forboth here .
*/
ndx = 0 ;
foreach ( lc , cteroot - > parse - > cteList )
{
CommonTableExpr * cte = ( CommonTableExpr * ) lfirst ( lc ) ;
if ( strcmp ( cte - > ctename , rte - > ctename ) = = 0 )
break ;
ndx + + ;
}
if ( lc = = NULL ) /* shouldn't happen */
elog ( ERROR , " could not find CTE \" %s \" " , rte - > ctename ) ;
if ( ndx > = list_length ( cteroot - > cte_plan_ids ) )
elog ( ERROR , " could not find plan for CTE \" %s \" " , rte - > ctename ) ;
plan_id = list_nth_int ( cteroot - > cte_plan_ids , ndx ) ;
if ( plan_id < = 0 )
elog ( ERROR , " no plan was made for CTE \" %s \" " , rte - > ctename ) ;
subroot = list_nth ( root - > glob - > subroots , plan_id - 1 ) ;
}
/* If the subquery hasn't been planned yet, we have to punt */
if ( subroot = = NULL )
return ;
Assert ( IsA ( subroot , PlannerInfo ) ) ;
/*
* We must use the subquery parsetree as mangled by the planner , not
* the raw version from the RTE , because we need a Var that will refer
* to the subroot ' s live RelOptInfos . For instance , if any subquery
* pullup happened during planning , Vars in the targetlist might have
* gotten replaced , and we need to see the replacement expressions .
*/
subquery = subroot - > parse ;
Assert ( IsA ( subquery , Query ) ) ;
/*
* Punt if subquery uses set operations or GROUP BY , as these will
* mash underlying columns ' stats beyond recognition . ( Set ops are
@ -5525,33 +5610,12 @@ examine_simple_variable(PlannerInfo *root, Var *var,
subquery - > groupingSets )
return ;
/*
* OK , fetch RelOptInfo for subquery . Note that we don ' t change the
* rel returned in vardata , since caller expects it to be a rel of the
* caller ' s query level . Because we might already be recursing , we
* can ' t use that rel pointer either , but have to look up the Var ' s
* rel afresh .
*/
rel = find_base_rel ( root , var - > varno ) ;
/* If the subquery hasn't been planned yet, we have to punt */
if ( rel - > subroot = = NULL )
return ;
Assert ( IsA ( rel - > subroot , PlannerInfo ) ) ;
/*
* Switch our attention to the subquery as mangled by the planner . It
* was okay to look at the pre - planning version for the tests above ,
* but now we need a Var that will refer to the subroot ' s live
* RelOptInfos . For instance , if any subquery pullup happened during
* planning , Vars in the targetlist might have gotten replaced , and we
* need to see the replacement expressions .
*/
subquery = rel - > subroot - > parse ;
Assert ( IsA ( subquery , Query ) ) ;
/* Get the subquery output expression referenced by the upper Var */
ste = get_tle_by_resno ( subquery - > targetList , var - > varattno ) ;
if ( subquery - > returningList )
subtlist = subquery - > returningList ;
else
subtlist = subquery - > targetList ;
ste = get_tle_by_resno ( subtlist , var - > varattno ) ;
if ( ste = = NULL | | ste - > resjunk )
elog ( ERROR , " subquery %s does not have attribute %d " ,
rte - > eref - > aliasname , var - > varattno ) ;
@ -5599,16 +5663,16 @@ examine_simple_variable(PlannerInfo *root, Var *var,
* if the underlying column is unique , the subquery may have
* joined to other tables in a way that creates duplicates .
*/
examine_simple_variable ( rel - > subroot , var , vardata ) ;
examine_simple_variable ( subroot , var , vardata ) ;
}
}
else
{
/*
* Otherwise , the Var comes from a FUNCTION , VALUES , or CTE RTE . ( We
* won ' t see RTE_JOIN here because join alias Vars have already been
* Otherwise , the Var comes from a FUNCTION or VALUES RTE . ( We won ' t
* see RTE_JOIN here because join alias Vars have already been
* flattened . ) There ' s not much we can do with function outputs , but
* maybe someday try to be smarter about VALUES and / or CTEs .
* maybe someday try to be smarter about VALUES .
*/
}
}