@ -444,7 +444,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
* Identify which baserestrictinfo clauses can be sent to the remote
* server and which can ' t .
*/
classifyConditions ( root , baserel ,
classifyConditions ( root , baserel , baserel - > baserestrictinfo ,
& fpinfo - > remote_conds , & fpinfo - > local_conds ) ;
/*
@ -540,12 +540,7 @@ postgresGetForeignPaths(PlannerInfo *root,
{
PgFdwRelationInfo * fpinfo = ( PgFdwRelationInfo * ) baserel - > fdw_private ;
ForeignPath * path ;
List * join_quals ;
Relids required_outer ;
double rows ;
int width ;
Cost startup_cost ;
Cost total_cost ;
List * ppi_list ;
ListCell * lc ;
/*
@ -573,15 +568,25 @@ postgresGetForeignPaths(PlannerInfo *root,
return ;
/*
* As a crude first hack , we consider each available join clause and try
* to make a parameterized path using just that clause . Later we should
* consider combinations of clauses , probably .
* Thumb through all join clauses for the rel to identify which outer
* relations could supply one or more safe - to - send - to - remote join clauses .
* We ' ll build a parameterized path for each such outer relation .
*
* It ' s convenient to manage this by representing each candidate outer
* relation by the ParamPathInfo node for it . We can then use the
* ppi_clauses list in the ParamPathInfo node directly as a list of the
* interesting join clauses for that rel . This takes care of the
* possibility that there are multiple safe join clauses for such a rel ,
* and also ensures that we account for unsafe join clauses that we ' ll
* still have to enforce locally ( since the parameterized - path machinery
* insists that we handle all movable clauses ) .
*/
/* Scan the rel's join clauses */
ppi_list = NIL ;
foreach ( lc , baserel - > joininfo )
{
RestrictInfo * rinfo = ( RestrictInfo * ) lfirst ( lc ) ;
Relids required_outer ;
ParamPathInfo * param_info ;
/* Check if clause can be moved to this rel */
if ( ! join_clause_is_movable_to ( rinfo , baserel ) )
@ -591,31 +596,29 @@ postgresGetForeignPaths(PlannerInfo *root,
if ( ! is_foreign_expr ( root , baserel , rinfo - > clause ) )
continue ;
/*
* OK , get a cost estimate from the remote , and make a path .
*/
join_quals = list_make1 ( rinfo ) ;
estimate_path_cost_size ( root , baserel , join_quals ,
& rows , & width ,
& startup_cost , & total_cost ) ;
/* Must calculate required outer rels for this path */
/* Calculate required outer rels for the resulting path */
required_outer = bms_union ( rinfo - > clause_relids ,
baserel - > lateral_relids ) ;
/* We do not want the foreign rel itself listed in required_outer */
required_outer = bms_del_member ( required_outer , baserel - > relid ) ;
/* Enforce convention that required_outer is exactly NULL if empty */
/*
* required_outer probably can ' t be empty here , but if it were , we
* couldn ' t make a parameterized path .
*/
if ( bms_is_empty ( required_outer ) )
required_outer = NULL ;
continue ;
path = create_foreignscan_path ( root , baserel ,
rows ,
startup_cost ,
total_cost ,
NIL , /* no pathkeys */
required_outer ,
NIL ) ; /* no fdw_private list */
add_path ( baserel , ( Path * ) path ) ;
/* Get the ParamPathInfo */
param_info = get_baserel_parampathinfo ( root , baserel ,
required_outer ) ;
Assert ( param_info ! = NULL ) ;
/*
* Add it to list unless we already have it . Testing pointer equality
* is OK since get_baserel_parampathinfo won ' t make duplicates .
*/
ppi_list = list_append_unique_ptr ( ppi_list , param_info ) ;
}
/*
@ -629,8 +632,8 @@ postgresGetForeignPaths(PlannerInfo *root,
* We repeatedly scan the eclass list looking for column references
* ( or expressions ) belonging to the foreign rel . Each time we find
* one , we generate a list of equivalence joinclauses for it , and then
* try to make those into foreign paths . Repeat till there are no
* more candidate EC members .
* see if any are safe to send to the remote . Repeat till there are
* no more candidate EC members .
*/
ec_member_foreign_arg arg ;
@ -658,6 +661,8 @@ postgresGetForeignPaths(PlannerInfo *root,
foreach ( lc , clauses )
{
RestrictInfo * rinfo = ( RestrictInfo * ) lfirst ( lc ) ;
Relids required_outer ;
ParamPathInfo * param_info ;
/* Check if clause can be moved to this rel */
if ( ! join_clause_is_movable_to ( rinfo , baserel ) )
@ -667,35 +672,60 @@ postgresGetForeignPaths(PlannerInfo *root,
if ( ! is_foreign_expr ( root , baserel , rinfo - > clause ) )
continue ;
/*
* OK , get a cost estimate from the remote , and make a path .
*/
join_quals = list_make1 ( rinfo ) ;
estimate_path_cost_size ( root , baserel , join_quals ,
& rows , & width ,
& startup_cost , & total_cost ) ;
/* Must calculate required outer rels for this path */
/* Calculate required outer rels for the resulting path */
required_outer = bms_union ( rinfo - > clause_relids ,
baserel - > lateral_relids ) ;
required_outer = bms_del_member ( required_outer , baserel - > relid ) ;
if ( bms_is_empty ( required_outer ) )
required_outer = NULL ;
path = create_foreignscan_path ( root , baserel ,
rows ,
startup_cost ,
total_cost ,
NIL , /* no pathkeys */
required_outer ,
NIL ) ; /* no fdw_private */
add_path ( baserel , ( Path * ) path ) ;
continue ;
/* Get the ParamPathInfo */
param_info = get_baserel_parampathinfo ( root , baserel ,
required_outer ) ;
Assert ( param_info ! = NULL ) ;
/* Add it to list unless we already have it */
ppi_list = list_append_unique_ptr ( ppi_list , param_info ) ;
}
/* Try again, now ignoring the expression we found this time */
arg . already_used = lappend ( arg . already_used , arg . current ) ;
}
}
/*
* Now build a path for each useful outer relation .
*/
foreach ( lc , ppi_list )
{
ParamPathInfo * param_info = ( ParamPathInfo * ) lfirst ( lc ) ;
double rows ;
int width ;
Cost startup_cost ;
Cost total_cost ;
/* Get a cost estimate from the remote */
estimate_path_cost_size ( root , baserel ,
param_info - > ppi_clauses ,
& rows , & width ,
& startup_cost , & total_cost ) ;
/*
* ppi_rows currently won ' t get looked at by anything , but still we
* may as well ensure that it matches our idea of the rowcount .
*/
param_info - > ppi_rows = rows ;
/* Make the path */
path = create_foreignscan_path ( root , baserel ,
rows ,
startup_cost ,
total_cost ,
NIL , /* no pathkeys */
param_info - > ppi_req_outer ,
NIL ) ; /* no fdw_private list */
add_path ( baserel , ( Path * ) path ) ;
}
}
/*
@ -725,14 +755,13 @@ postgresGetForeignPlan(PlannerInfo *root,
* those that can ' t . baserestrictinfo clauses that were previously
* determined to be safe or unsafe by classifyConditions are shown in
* fpinfo - > remote_conds and fpinfo - > local_conds . Anything else in the
* scan_clauses list should be a join clause that was found safe by
* postgresGetForeignPaths .
* scan_clauses list will be a join clause , which we have to check for
* remote - safety .
*
* Note : for clauses extracted from EquivalenceClasses , it ' s possible that
* what we get here is a different representation of the clause than what
* postgresGetForeignPaths saw ; for example we might get a commuted
* version of the clause . So we can ' t insist on simple equality as we do
* for the baserestrictinfo clauses .
* Note : the join clauses we see here should be the exact same ones
* previously examined by postgresGetForeignPaths . Possibly it ' d be worth
* passing forward the classification work done then , rather than
* repeating it here .
*
* This code must match " extract_actual_clauses(scan_clauses, false) "
* except for the additional decision about remote versus local execution .
@ -754,11 +783,10 @@ postgresGetForeignPlan(PlannerInfo *root,
remote_conds = lappend ( remote_conds , rinfo ) ;
else if ( list_member_ptr ( fpinfo - > local_conds , rinfo ) )
local_exprs = lappend ( local_exprs , rinfo - > clause ) ;
else
{
Assert ( is_foreign_expr ( root , baserel , rinfo - > clause ) ) ;
else if ( is_foreign_expr ( root , baserel , rinfo - > clause ) )
remote_conds = lappend ( remote_conds , rinfo ) ;
}
else
local_exprs = lappend ( local_exprs , rinfo - > clause ) ;
}
/*
@ -1689,9 +1717,20 @@ estimate_path_cost_size(PlannerInfo *root,
*/
if ( fpinfo - > use_remote_estimate )
{
List * remote_join_conds ;
List * local_join_conds ;
StringInfoData sql ;
List * retrieved_attrs ;
PGconn * conn ;
Selectivity local_sel ;
QualCost local_cost ;
/*
* join_conds might contain both clauses that are safe to send across ,
* and clauses that aren ' t .
*/
classifyConditions ( root , baserel , join_conds ,
& remote_join_conds , & local_join_conds ) ;
/*
* Construct EXPLAIN query including the desired SELECT , FROM , and
@ -1705,8 +1744,8 @@ estimate_path_cost_size(PlannerInfo *root,
if ( fpinfo - > remote_conds )
appendWhereClause ( & sql , root , baserel , fpinfo - > remote_conds ,
true , NULL ) ;
if ( join_conds )
appendWhereClause ( & sql , root , baserel , join_conds ,
if ( remote_ join_conds)
appendWhereClause ( & sql , root , baserel , remote_ join_conds,
( fpinfo - > remote_conds = = NIL ) , NULL ) ;
/* Get the remote estimate */
@ -1717,12 +1756,22 @@ estimate_path_cost_size(PlannerInfo *root,
retrieved_rows = rows ;
/* Factor in the selectivity of the local_conds */
rows = clamp_row_est ( rows * fpinfo - > local_conds_sel ) ;
/* Factor in the selectivity of the locally-checked quals */
local_sel = clauselist_selectivity ( root ,
local_join_conds ,
baserel - > relid ,
JOIN_INNER ,
NULL ) ;
local_sel * = fpinfo - > local_conds_sel ;
rows = clamp_row_est ( rows * local_sel ) ;
/* Add in the eval cost of the local_conds */
/* Add in the eval cost of the locally-checked qual s */
startup_cost + = fpinfo - > local_conds_cost . startup ;
total_cost + = fpinfo - > local_conds_cost . per_tuple * retrieved_rows ;
cost_qual_eval ( & local_cost , local_join_conds , root ) ;
startup_cost + = local_cost . startup ;
total_cost + = local_cost . per_tuple * retrieved_rows ;
}
else
{