1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-12 21:01:52 +03:00

Code review for foreign/custom join pushdown patch.

Commit e7cb7ee145 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments.  Clean up
as follows:

* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function.  In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs.  Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.

* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.

* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that.  Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.

* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries.  The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks.  It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway.  I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.

* Avoid ad-hocery in ExecAssignScanProjectionInfo.  It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.

* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.

* Lots of cleanup of documentation and missed comments.  Re-order some
code additions into more logical places.
This commit is contained in:
Tom Lane
2015-05-10 14:36:30 -04:00
parent c594c75078
commit 1a8a4e5cde
26 changed files with 701 additions and 653 deletions

View File

@ -30,21 +30,13 @@ set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype, SpecialJoinInfo *sjinfo,
Relids param_source_rels, Relids extra_lateral_rels);
JoinType jointype, JoinPathExtraData *extra);
static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype, SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels, Relids extra_lateral_rels);
JoinType jointype, JoinPathExtraData *extra);
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype, SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels, Relids extra_lateral_rels);
JoinType jointype, JoinPathExtraData *extra);
static List *select_mergejoin_clauses(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
@ -86,13 +78,16 @@ add_paths_to_joinrel(PlannerInfo *root,
SpecialJoinInfo *sjinfo,
List *restrictlist)
{
List *mergeclause_list = NIL;
JoinPathExtraData extra;
bool mergejoin_allowed = true;
SemiAntiJoinFactors semifactors;
Relids param_source_rels = NULL;
Relids extra_lateral_rels = NULL;
ListCell *lc;
extra.restrictlist = restrictlist;
extra.mergeclause_list = NIL;
extra.sjinfo = sjinfo;
extra.param_source_rels = NULL;
extra.extra_lateral_rels = NULL;
/*
* Find potential mergejoin clauses. We can skip this if we are not
* interested in doing a mergejoin. However, mergejoin may be our only
@ -100,13 +95,13 @@ add_paths_to_joinrel(PlannerInfo *root,
* it's a full join.
*/
if (enable_mergejoin || jointype == JOIN_FULL)
mergeclause_list = select_mergejoin_clauses(root,
joinrel,
outerrel,
innerrel,
restrictlist,
jointype,
&mergejoin_allowed);
extra.mergeclause_list = select_mergejoin_clauses(root,
joinrel,
outerrel,
innerrel,
restrictlist,
jointype,
&mergejoin_allowed);
/*
* If it's SEMI or ANTI join, compute correction factors for cost
@ -115,7 +110,7 @@ add_paths_to_joinrel(PlannerInfo *root,
if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
compute_semi_anti_join_factors(root, outerrel, innerrel,
jointype, sjinfo, restrictlist,
&semifactors);
&extra.semifactors);
/*
* Decide whether it's sensible to generate parameterized paths for this
@ -142,16 +137,16 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (bms_overlap(joinrel->relids, sjinfo->min_righthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_lefthand))
param_source_rels = bms_join(param_source_rels,
bms_difference(root->all_baserels,
extra.param_source_rels = bms_join(extra.param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_righthand));
/* full joins constrain both sides symmetrically */
if (sjinfo->jointype == JOIN_FULL &&
bms_overlap(joinrel->relids, sjinfo->min_lefthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_righthand))
param_source_rels = bms_join(param_source_rels,
bms_difference(root->all_baserels,
extra.param_source_rels = bms_join(extra.param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_lefthand));
}
@ -168,9 +163,9 @@ add_paths_to_joinrel(PlannerInfo *root,
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
if (bms_is_subset(ljinfo->lateral_rhs, joinrel->relids))
param_source_rels = bms_join(param_source_rels,
bms_difference(ljinfo->lateral_lhs,
joinrel->relids));
extra.param_source_rels = bms_join(extra.param_source_rels,
bms_difference(ljinfo->lateral_lhs,
joinrel->relids));
}
/*
@ -195,8 +190,8 @@ add_paths_to_joinrel(PlannerInfo *root,
!bms_is_subset(phinfo->ph_eval_at, innerrel->relids))
{
/* Yes, remember its lateral rels */
extra_lateral_rels = bms_add_members(extra_lateral_rels,
phinfo->ph_lateral);
extra.extra_lateral_rels = bms_add_members(extra.extra_lateral_rels,
phinfo->ph_lateral);
}
}
@ -206,9 +201,10 @@ add_paths_to_joinrel(PlannerInfo *root,
* it to required_outer below, while preserving the property that
* required_outer is exactly NULL if empty.)
*/
extra_lateral_rels = bms_del_members(extra_lateral_rels, joinrel->relids);
if (bms_is_empty(extra_lateral_rels))
extra_lateral_rels = NULL;
extra.extra_lateral_rels = bms_del_members(extra.extra_lateral_rels,
joinrel->relids);
if (bms_is_empty(extra.extra_lateral_rels))
extra.extra_lateral_rels = NULL;
/*
* 1. Consider mergejoin paths where both relations must be explicitly
@ -216,9 +212,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo,
param_source_rels, extra_lateral_rels);
jointype, &extra);
/*
* 2. Consider paths where the outer relation need not be explicitly
@ -229,9 +223,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
match_unsorted_outer(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo, &semifactors,
param_source_rels, extra_lateral_rels);
jointype, &extra);
#ifdef NOT_USED
@ -248,9 +240,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
match_unsorted_inner(root, joinrel, outerrel, innerrel,
restrictlist, mergeclause_list, jointype,
sjinfo, &semifactors,
param_source_rels, extra_lateral_rels);
jointype, &extra);
#endif
/*
@ -260,30 +250,24 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (enable_hashjoin || jointype == JOIN_FULL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, jointype,
sjinfo, &semifactors,
param_source_rels, extra_lateral_rels);
jointype, &extra);
/*
* 5. If both inner and outer relations are managed by the same FDW,
* give it a chance to push down joins.
* 5. If inner and outer relations are foreign tables (or joins) belonging
* to the same server, give the FDW a chance to push down joins.
*/
if (joinrel->fdwroutine &&
joinrel->fdwroutine->GetForeignJoinPaths)
joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel,
outerrel, innerrel,
restrictlist, jointype, sjinfo,
&semifactors,
param_source_rels,
extra_lateral_rels);
jointype, &extra);
/*
* 6. Finally, give extensions a chance to manipulate the path list.
*/
if (set_join_pathlist_hook)
set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
restrictlist, jointype,
sjinfo, &semifactors,
param_source_rels, extra_lateral_rels);
jointype, &extra);
}
/*
@ -294,15 +278,11 @@ add_paths_to_joinrel(PlannerInfo *root,
static void
try_nestloop_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys)
List *pathkeys,
JoinType jointype,
JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@ -314,7 +294,7 @@ try_nestloop_path(PlannerInfo *root,
required_outer = calc_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
!bms_overlap(required_outer, param_source_rels))
!bms_overlap(required_outer, extra->param_source_rels))
{
/*
* We override the param_source_rels heuristic to accept nestloop
@ -345,7 +325,7 @@ try_nestloop_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
required_outer = bms_add_members(required_outer, extra_lateral_rels);
required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* Do a precheck to quickly eliminate obviously-inferior paths. We
@ -358,7 +338,7 @@ try_nestloop_path(PlannerInfo *root,
*/
initial_cost_nestloop(root, &workspace, jointype,
outer_path, inner_path,
sjinfo, semifactors);
extra->sjinfo, &extra->semifactors);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@ -369,11 +349,11 @@ try_nestloop_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
sjinfo,
semifactors,
extra->sjinfo,
&extra->semifactors,
outer_path,
inner_path,
restrict_clauses,
extra->restrictlist,
pathkeys,
required_outer));
}
@ -392,17 +372,14 @@ try_nestloop_path(PlannerInfo *root,
static void
try_mergejoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
Relids param_source_rels,
Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
List *outersortkeys,
List *innersortkeys)
List *innersortkeys,
JoinType jointype,
JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@ -414,7 +391,7 @@ try_mergejoin_path(PlannerInfo *root,
required_outer = calc_non_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
!bms_overlap(required_outer, param_source_rels))
!bms_overlap(required_outer, extra->param_source_rels))
{
/* Waste no memory when we reject a path here */
bms_free(required_outer);
@ -425,7 +402,7 @@ try_mergejoin_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
required_outer = bms_add_members(required_outer, extra_lateral_rels);
required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* If the given paths are already well enough ordered, we can skip doing
@ -444,7 +421,7 @@ try_mergejoin_path(PlannerInfo *root,
initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
outer_path, inner_path,
outersortkeys, innersortkeys,
sjinfo);
extra->sjinfo);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@ -455,10 +432,10 @@ try_mergejoin_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
sjinfo,
extra->sjinfo,
outer_path,
inner_path,
restrict_clauses,
extra->restrictlist,
pathkeys,
required_outer,
mergeclauses,
@ -480,15 +457,11 @@ try_mergejoin_path(PlannerInfo *root,
static void
try_hashjoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *hashclauses)
List *hashclauses,
JoinType jointype,
JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@ -500,7 +473,7 @@ try_hashjoin_path(PlannerInfo *root,
required_outer = calc_non_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
!bms_overlap(required_outer, param_source_rels))
!bms_overlap(required_outer, extra->param_source_rels))
{
/* Waste no memory when we reject a path here */
bms_free(required_outer);
@ -511,7 +484,7 @@ try_hashjoin_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
required_outer = bms_add_members(required_outer, extra_lateral_rels);
required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* See comments in try_nestloop_path(). Also note that hashjoin paths
@ -519,7 +492,7 @@ try_hashjoin_path(PlannerInfo *root,
*/
initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
outer_path, inner_path,
sjinfo, semifactors);
extra->sjinfo, &extra->semifactors);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@ -530,11 +503,11 @@ try_hashjoin_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
sjinfo,
semifactors,
extra->sjinfo,
&extra->semifactors,
outer_path,
inner_path,
restrict_clauses,
extra->restrictlist,
required_outer,
hashclauses));
}
@ -584,26 +557,16 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
* 'jointype' is the type of join to do
* 'sjinfo' is extra info about the join for selectivity estimation
* 'param_source_rels' are OK targets for parameterization of result paths
* 'extra_lateral_rels' are additional parameterization for result paths
* 'extra' contains additional input values
*/
static void
sort_inner_and_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
List *mergeclause_list,
JoinType jointype,
SpecialJoinInfo *sjinfo,
Relids param_source_rels,
Relids extra_lateral_rels)
JoinPathExtraData *extra)
{
Path *outer_path;
Path *inner_path;
@ -643,14 +606,14 @@ sort_inner_and_outer(PlannerInfo *root,
if (jointype == JOIN_UNIQUE_OUTER)
{
outer_path = (Path *) create_unique_path(root, outerrel,
outer_path, sjinfo);
outer_path, extra->sjinfo);
Assert(outer_path);
jointype = JOIN_INNER;
}
else if (jointype == JOIN_UNIQUE_INNER)
{
inner_path = (Path *) create_unique_path(root, innerrel,
inner_path, sjinfo);
inner_path, extra->sjinfo);
Assert(inner_path);
jointype = JOIN_INNER;
}
@ -684,7 +647,7 @@ sort_inner_and_outer(PlannerInfo *root,
* exactly as-is as well as making variants.
*/
all_pathkeys = select_outer_pathkeys_for_merge(root,
mergeclause_list,
extra->mergeclause_list,
joinrel);
foreach(l, all_pathkeys)
@ -707,10 +670,10 @@ sort_inner_and_outer(PlannerInfo *root,
cur_mergeclauses = find_mergeclauses_for_pathkeys(root,
outerkeys,
true,
mergeclause_list);
extra->mergeclause_list);
/* Should have used them all... */
Assert(list_length(cur_mergeclauses) == list_length(mergeclause_list));
Assert(list_length(cur_mergeclauses) == list_length(extra->mergeclause_list));
/* Build sort pathkeys for the inner side */
innerkeys = make_inner_pathkeys_for_merge(root,
@ -730,17 +693,14 @@ sort_inner_and_outer(PlannerInfo *root,
*/
try_mergejoin_path(root,
joinrel,
jointype,
sjinfo,
param_source_rels,
extra_lateral_rels,
outer_path,
inner_path,
restrictlist,
merge_pathkeys,
cur_mergeclauses,
outerkeys,
innerkeys);
innerkeys,
jointype,
extra);
}
}
@ -771,28 +731,16 @@ sort_inner_and_outer(PlannerInfo *root,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
* 'jointype' is the type of join to do
* 'sjinfo' is extra info about the join for selectivity estimation
* 'semifactors' contains valid data if jointype is SEMI or ANTI
* 'param_source_rels' are OK targets for parameterization of result paths
* 'extra_lateral_rels' are additional parameterization for result paths
* 'extra' contains additional input values
*/
static void
match_unsorted_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
List *mergeclause_list,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels)
JoinPathExtraData *extra)
{
JoinType save_jointype = jointype;
bool nestjoinOK;
@ -854,7 +802,7 @@ match_unsorted_outer(PlannerInfo *root,
if (inner_cheapest_total == NULL)
return;
inner_cheapest_total = (Path *)
create_unique_path(root, innerrel, inner_cheapest_total, sjinfo);
create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo);
Assert(inner_cheapest_total);
}
else if (nestjoinOK)
@ -898,7 +846,7 @@ match_unsorted_outer(PlannerInfo *root,
if (outerpath != outerrel->cheapest_total_path)
continue;
outerpath = (Path *) create_unique_path(root, outerrel,
outerpath, sjinfo);
outerpath, extra->sjinfo);
Assert(outerpath);
}
@ -918,15 +866,11 @@ match_unsorted_outer(PlannerInfo *root,
*/
try_nestloop_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
outerpath,
inner_cheapest_total,
restrictlist,
merge_pathkeys);
merge_pathkeys,
jointype,
extra);
}
else if (nestjoinOK)
{
@ -944,30 +888,22 @@ match_unsorted_outer(PlannerInfo *root,
try_nestloop_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
outerpath,
innerpath,
restrictlist,
merge_pathkeys);
merge_pathkeys,
jointype,
extra);
}
/* Also consider materialized form of the cheapest inner path */
if (matpath != NULL)
try_nestloop_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
outerpath,
matpath,
restrictlist,
merge_pathkeys);
merge_pathkeys,
jointype,
extra);
}
/* Can't do anything else if outer path needs to be unique'd */
@ -982,7 +918,7 @@ match_unsorted_outer(PlannerInfo *root,
mergeclauses = find_mergeclauses_for_pathkeys(root,
outerpath->pathkeys,
true,
mergeclause_list);
extra->mergeclause_list);
/*
* Done with this outer path if no chance for a mergejoin.
@ -1000,7 +936,7 @@ match_unsorted_outer(PlannerInfo *root,
else
continue;
}
if (useallclauses && list_length(mergeclauses) != list_length(mergeclause_list))
if (useallclauses && list_length(mergeclauses) != list_length(extra->mergeclause_list))
continue;
/* Compute the required ordering of the inner path */
@ -1016,17 +952,14 @@ match_unsorted_outer(PlannerInfo *root,
*/
try_mergejoin_path(root,
joinrel,
jointype,
sjinfo,
param_source_rels,
extra_lateral_rels,
outerpath,
inner_cheapest_total,
restrictlist,
merge_pathkeys,
mergeclauses,
NIL,
innersortkeys);
innersortkeys,
jointype,
extra);
/* Can't do anything else if inner path needs to be unique'd */
if (save_jointype == JOIN_UNIQUE_INNER)
@ -1115,17 +1048,14 @@ match_unsorted_outer(PlannerInfo *root,
newclauses = mergeclauses;
try_mergejoin_path(root,
joinrel,
jointype,
sjinfo,
param_source_rels,
extra_lateral_rels,
outerpath,
innerpath,
restrictlist,
merge_pathkeys,
newclauses,
NIL,
NIL);
NIL,
jointype,
extra);
cheapest_total_inner = innerpath;
}
/* Same on the basis of cheapest startup cost ... */
@ -1161,17 +1091,14 @@ match_unsorted_outer(PlannerInfo *root,
}
try_mergejoin_path(root,
joinrel,
jointype,
sjinfo,
param_source_rels,
extra_lateral_rels,
outerpath,
innerpath,
restrictlist,
merge_pathkeys,
newclauses,
NIL,
NIL);
NIL,
jointype,
extra);
}
cheapest_startup_inner = innerpath;
}
@ -1193,25 +1120,16 @@ match_unsorted_outer(PlannerInfo *root,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
* 'jointype' is the type of join to do
* 'sjinfo' is extra info about the join for selectivity estimation
* 'semifactors' contains valid data if jointype is SEMI or ANTI
* 'param_source_rels' are OK targets for parameterization of result paths
* 'extra_lateral_rels' are additional parameterization for result paths
* 'extra' contains additional input values
*/
static void
hash_inner_and_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels)
JoinPathExtraData *extra)
{
bool isouterjoin = IS_OUTER_JOIN(jointype);
List *hashclauses;
@ -1225,7 +1143,7 @@ hash_inner_and_outer(PlannerInfo *root,
* usable with this pair of sub-relations.
*/
hashclauses = NIL;
foreach(l, restrictlist)
foreach(l, extra->restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
@ -1276,53 +1194,41 @@ hash_inner_and_outer(PlannerInfo *root,
{
cheapest_total_outer = (Path *)
create_unique_path(root, outerrel,
cheapest_total_outer, sjinfo);
cheapest_total_outer, extra->sjinfo);
Assert(cheapest_total_outer);
jointype = JOIN_INNER;
try_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
cheapest_total_outer,
cheapest_total_inner,
restrictlist,
hashclauses);
hashclauses,
jointype,
extra);
/* no possibility of cheap startup here */
}
else if (jointype == JOIN_UNIQUE_INNER)
{
cheapest_total_inner = (Path *)
create_unique_path(root, innerrel,
cheapest_total_inner, sjinfo);
cheapest_total_inner, extra->sjinfo);
Assert(cheapest_total_inner);
jointype = JOIN_INNER;
try_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
cheapest_total_outer,
cheapest_total_inner,
restrictlist,
hashclauses);
hashclauses,
jointype,
extra);
if (cheapest_startup_outer != NULL &&
cheapest_startup_outer != cheapest_total_outer)
try_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
cheapest_startup_outer,
cheapest_total_inner,
restrictlist,
hashclauses);
hashclauses,
jointype,
extra);
}
else
{
@ -1339,15 +1245,11 @@ hash_inner_and_outer(PlannerInfo *root,
if (cheapest_startup_outer != NULL)
try_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
cheapest_startup_outer,
cheapest_total_inner,
restrictlist,
hashclauses);
hashclauses,
jointype,
extra);
foreach(lc1, outerrel->cheapest_parameterized_paths)
{
@ -1377,15 +1279,11 @@ hash_inner_and_outer(PlannerInfo *root,
try_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
semifactors,
param_source_rels,
extra_lateral_rels,
outerpath,
innerpath,
restrictlist,
hashclauses);
hashclauses,
jointype,
extra);
}
}
}

View File

@ -44,6 +44,7 @@
#include "utils/lsyscache.h"
static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
static List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
@ -219,7 +220,7 @@ create_plan(PlannerInfo *root, Path *best_path)
* create_plan_recurse
* Recursive guts of create_plan().
*/
Plan *
static Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path)
{
Plan *plan;
@ -1950,7 +1951,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
/*
* create_foreignscan_plan
* Returns a foreignscan plan for the base relation scanned by 'best_path'
* Returns a foreignscan plan for the relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static ForeignScan *
@ -1965,9 +1966,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
ListCell *lc;
int i;
Assert(rel->fdwroutine != NULL);
/*
* If we're scanning a base relation, look up the OID.
* (We can skip this if scanning a join relation.)
* If we're scanning a base relation, fetch its OID. (Irrelevant if
* scanning a join relation.)
*/
if (scan_relid > 0)
{
@ -1978,7 +1981,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
Assert(rte->rtekind == RTE_RELATION);
rel_oid = rte->relid;
}
Assert(rel->fdwroutine != NULL);
/*
* Sort clauses into best execution order. We do this first since the FDW
@ -1996,42 +1998,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
best_path,
tlist, scan_clauses);
/*
* Sanity check. There may be resjunk entries in fdw_ps_tlist that
* are included only to help EXPLAIN deparse plans properly. We require
* that these are at the end, so that when the executor builds the scan
* descriptor based on the non-junk entries, it gets the attribute
* numbers correct.
*/
if (scan_plan->scan.scanrelid == 0)
{
bool found_resjunk = false;
foreach (lc, scan_plan->fdw_ps_tlist)
{
TargetEntry *tle = lfirst(lc);
if (tle->resjunk)
found_resjunk = true;
else if (found_resjunk)
elog(ERROR, "junk TLE should not apper prior to valid one");
}
}
/* Set the relids that are represented by this foreign scan for Explain */
scan_plan->fdw_relids = best_path->path.parent->relids;
/* Copy cost data from Path to Plan; no need to make FDW do this */
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
/* Track FDW server-id; no need to make FDW do this */
scan_plan->fdw_handler = rel->fdw_handler;
/* Copy foreign server OID; likewise, no need to make FDW do this */
scan_plan->fs_server = rel->serverid;
/* Likewise, copy the relids that are represented by this foreign scan */
scan_plan->fs_relids = best_path->path.parent->relids;
/*
* Replace any outer-relation variables with nestloop params in the qual
* and fdw_exprs expressions. We do this last so that the FDW doesn't
* have to be involved. (Note that parts of fdw_exprs could have come
* from join clauses, so doing this beforehand on the scan_clauses
* wouldn't work.)
* wouldn't work.) We assume fdw_scan_tlist contains no such variables.
*/
if (best_path->path.param_info)
{
@ -2087,7 +2069,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
{
CustomScan *cplan;
RelOptInfo *rel = best_path->path.parent;
ListCell *lc;
/*
* Sort clauses into the best execution order, although custom-scan
@ -2106,42 +2087,22 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
scan_clauses);
Assert(IsA(cplan, CustomScan));
/*
* Sanity check. There may be resjunk entries in custom_ps_tlist that
* are included only to help EXPLAIN deparse plans properly. We require
* that these are at the end, so that when the executor builds the scan
* descriptor based on the non-junk entries, it gets the attribute
* numbers correct.
*/
if (cplan->scan.scanrelid == 0)
{
bool found_resjunk = false;
foreach (lc, cplan->custom_ps_tlist)
{
TargetEntry *tle = lfirst(lc);
if (tle->resjunk)
found_resjunk = true;
else if (found_resjunk)
elog(ERROR, "junk TLE should not apper prior to valid one");
}
}
/* Set the relids that are represented by this custom scan for Explain */
cplan->custom_relids = best_path->path.parent->relids;
/*
* Copy cost data from Path to Plan; no need to make custom-plan providers
* do this
*/
copy_path_costsize(&cplan->scan.plan, &best_path->path);
/* Likewise, copy the relids that are represented by this custom scan */
cplan->custom_relids = best_path->path.parent->relids;
/*
* Replace any outer-relation variables with nestloop params in the qual
* and custom_exprs expressions. We do this last so that the custom-plan
* provider doesn't have to be involved. (Note that parts of custom_exprs
* could have come from join clauses, so doing this beforehand on the
* scan_clauses wouldn't work.)
* scan_clauses wouldn't work.) We assume custom_scan_tlist contains no
* such variables.
*/
if (best_path->path.param_info)
{
@ -3611,7 +3572,8 @@ make_foreignscan(List *qptlist,
List *qpqual,
Index scanrelid,
List *fdw_exprs,
List *fdw_private)
List *fdw_private,
List *fdw_scan_tlist)
{
ForeignScan *node = makeNode(ForeignScan);
Plan *plan = &node->scan.plan;
@ -3622,8 +3584,13 @@ make_foreignscan(List *qptlist,
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
/* fs_server will be filled in by create_foreignscan_plan */
node->fs_server = InvalidOid;
node->fdw_exprs = fdw_exprs;
node->fdw_private = fdw_private;
node->fdw_scan_tlist = fdw_scan_tlist;
/* fs_relids will be filled in by create_foreignscan_plan */
node->fs_relids = NULL;
/* fsSystemCol will be filled in by create_foreignscan_plan */
node->fsSystemCol = false;

View File

@ -86,12 +86,6 @@ static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
static void set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset);
static void set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset);
static Plan *set_indexonlyscan_references(PlannerInfo *root,
IndexOnlyScan *plan,
int rtoffset);
@ -99,6 +93,12 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan,
int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan);
static void set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset);
static void set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
@ -573,7 +573,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
case T_ForeignScan:
set_foreignscan_references(root, (ForeignScan *) plan, rtoffset);
break;
case T_CustomScan:
set_customscan_references(root, (CustomScan *) plan, rtoffset);
break;
@ -890,121 +889,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
return plan;
}
/*
* set_foreignscan_references
* Do set_plan_references processing on an ForeignScan
*/
static void
set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset)
{
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(fscan->fdw_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
fscan->fdw_relids = tempset;
}
if (fscan->scan.scanrelid == 0)
{
indexed_tlist *pscan_itlist = build_tlist_index(fscan->fdw_ps_tlist);
fscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.targetlist,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.qual,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_exprs,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_ps_tlist =
fix_scan_list(root, fscan->fdw_ps_tlist, rtoffset);
pfree(pscan_itlist);
}
else
{
fscan->scan.scanrelid += rtoffset;
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs, rtoffset);
}
}
/*
* set_customscan_references
* Do set_plan_references processing on an CustomScan
*/
static void
set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset)
{
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
cscan->custom_relids = tempset;
}
if (cscan->scan.scanrelid == 0)
{
indexed_tlist *pscan_itlist =
build_tlist_index(cscan->custom_ps_tlist);
cscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.targetlist,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.qual,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->custom_exprs = (List *)
fix_upper_expr(root,
(Node *) cscan->custom_exprs,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->custom_ps_tlist =
fix_scan_list(root, cscan->custom_ps_tlist, rtoffset);
pfree(pscan_itlist);
}
else
{
cscan->scan.scanrelid += rtoffset;
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs, rtoffset);
}
}
/*
* set_indexonlyscan_references
* Do set_plan_references processing on an IndexOnlyScan
@ -1179,6 +1063,134 @@ trivial_subqueryscan(SubqueryScan *plan)
return true;
}
/*
* set_foreignscan_references
* Do set_plan_references processing on a ForeignScan
*/
static void
set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset)
{
/* Adjust scanrelid if it's valid */
if (fscan->scan.scanrelid > 0)
fscan->scan.scanrelid += rtoffset;
if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0)
{
/* Adjust tlist, qual, fdw_exprs to reference custom scan tuple */
indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist);
fscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
rtoffset);
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.qual,
itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_exprs,
itlist,
INDEX_VAR,
rtoffset);
pfree(itlist);
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
}
else
{
/* Adjust tlist, qual, fdw_exprs in the standard way */
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs, rtoffset);
}
/* Adjust fs_relids if needed */
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(fscan->fs_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
fscan->fs_relids = tempset;
}
}
/*
* set_customscan_references
* Do set_plan_references processing on a CustomScan
*/
static void
set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset)
{
/* Adjust scanrelid if it's valid */
if (cscan->scan.scanrelid > 0)
cscan->scan.scanrelid += rtoffset;
if (cscan->custom_scan_tlist != NIL || cscan->scan.scanrelid == 0)
{
/* Adjust tlist, qual, custom_exprs to reference custom scan tuple */
indexed_tlist *itlist = build_tlist_index(cscan->custom_scan_tlist);
cscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
rtoffset);
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.qual,
itlist,
INDEX_VAR,
rtoffset);
cscan->custom_exprs = (List *)
fix_upper_expr(root,
(Node *) cscan->custom_exprs,
itlist,
INDEX_VAR,
rtoffset);
pfree(itlist);
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs, rtoffset);
}
/* Adjust custom_relids if needed */
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
cscan->custom_relids = tempset;
}
}
/*
* copyVar
* Copy a Var node.

View File

@ -2318,12 +2318,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
case T_ForeignScan:
finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
&context);
/* We assume fdw_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_CustomScan:
finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
&context);
/* We assume custom_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params);
break;

View File

@ -380,17 +380,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
rel->indexlist = indexinfos;
/* Grab the fdwroutine info using the relcache, while we have it */
/* Grab foreign-table info using the relcache, while we have it */
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{
rel->fdw_handler = GetFdwHandlerByRelId(RelationGetRelid(relation));
rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
}
else
{
rel->fdw_handler = InvalidOid;
rel->serverid = InvalidOid;
rel->fdwroutine = NULL;
}
heap_close(relation, NoLock);
/*

View File

@ -14,7 +14,6 @@
*/
#include "postgres.h"
#include "foreign/fdwapi.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
@ -122,8 +121,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->subplan = NULL;
rel->subroot = NULL;
rel->subplan_params = NIL;
rel->serverid = InvalidOid;
rel->fdwroutine = NULL;
rel->fdw_handler = InvalidOid;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
@ -385,6 +384,7 @@ build_join_rel(PlannerInfo *root,
joinrel->subplan = NULL;
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
joinrel->serverid = InvalidOid;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;
@ -393,6 +393,17 @@ build_join_rel(PlannerInfo *root,
joinrel->joininfo = NIL;
joinrel->has_eclass_joins = false;
/*
* Set up foreign-join fields if outer and inner relation are foreign
* tables (or joins) belonging to the same server.
*/
if (OidIsValid(outer_rel->serverid) &&
inner_rel->serverid == outer_rel->serverid)
{
joinrel->serverid = outer_rel->serverid;
joinrel->fdwroutine = outer_rel->fdwroutine;
}
/*
* Create a new tlist containing just the vars that need to be output from
* this join (ie, are needed for higher joinclauses or final output).
@ -428,18 +439,6 @@ build_join_rel(PlannerInfo *root,
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
sjinfo, restrictlist);
/*
* Set FDW handler and routine if both outer and inner relation
* are managed by same FDW driver.
*/
if (OidIsValid(outer_rel->fdw_handler) &&
OidIsValid(inner_rel->fdw_handler) &&
outer_rel->fdw_handler == inner_rel->fdw_handler)
{
joinrel->fdw_handler = outer_rel->fdw_handler;
joinrel->fdwroutine = GetFdwRoutine(joinrel->fdw_handler);
}
/*
* Add the joinrel to the query's joinrel list, and store it into the
* auxiliary hashtable if there is one. NB: GEQO requires us to append