进入 postgresGetForeignPaths()
在 postgresGetForeignRelSize() 中已经对最基本的外部表扫描做了成本估算,所以就用这些信息,直接生成一个 plan path:
/*
* Create simplest ForeignScan path node and add it to baserel. This path
* corresponds to SeqScan path of regular tables (though depending on what
* baserestrict conditions we were able to send to remote, there might
* actually be an indexscan happening there). We already did all the work
* to estimate cost and size of this path.
*/
path = create_foreignscan_path(root, baserel,
fpinfo->rows,
fpinfo->startup_cost,
fpinfo->total_cost,
NIL, /* no pathkeys */
NULL, /* no outer rel either */
NULL, /* no extra plan */
NIL); /* no fdw_private list */
然后将这个 path 加入 RelOptInfo *baserel 中的 pathlist 中,接下来可能会生成其他的 plan path,但每个 path 中的 parent 都指向这个 RelOptInfo *baserel :
add_path(baserel, (Path *) path);
只有当外部表设定了 remote estimates,才会接着计算 parameterized join 相关的 plan path:
/*
* If we're not using remote estimates, stop here. We have no way to
* estimate whether any join clauses would be worth sending across, so
* don't bother building parameterized paths.
*/
if (!fpinfo->use_remote_estimate)
return;
关于 join path 的实际计算过程暂不研究,其中值得注意的是:
join path 的计算结果存放在 param_info 中,并挂在 ppi_list 的链表上。
/* 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);
每一种 join 的情况,都对应一个挂在 ppi_list 上的 param_info,对于每一个 param_info 都生成一个 path,与上面的基本扫描相比,它们都带上了 param_info->ppi_req_outer。
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,
NULL,
NIL); /* no fdw_private list */
add_path(baserel, (Path *) path);
}
最后,在系统根据选出的最佳 path 生成 plan 的函数 create_scan_plan() (createplan.c)中,
scan_clauses 是准备发送给执行器执行语句的限制条件,这里如果存在 best_path->param_info 的话,就会将其中带有的 join 限制条件也加入 scan_clauses。
/*
* Extract the relevant restriction clauses from the parent relation. The
* executor must apply all these restrictions during the scan, except for
* pseudoconstants which we'll take care of below.
*/
scan_clauses = rel->baserestrictinfo;
/*
* If this is a parameterized scan, we also need to enforce all the join
* clauses available from the outer relation(s).
*
* For paranoia's sake, don't modify the stored baserestrictinfo list.
*/
if (best_path->param_info)
scan_clauses = list_concat(list_copy(scan_clauses),
best_path->param_info->ppi_clauses);