1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-15 19:21:59 +03:00

Use parameterized paths to generate inner indexscans more flexibly.

This patch fixes the planner so that it can generate nestloop-with-
inner-indexscan plans even with one or more levels of joining between
the indexscan and the nestloop join that is supplying the parameter.
The executor was fixed to handle such cases some time ago, but the
planner was not ready.  This should improve our plans in many situations
where join ordering restrictions formerly forced complete table scans.

There is probably a fair amount of tuning work yet to be done, because
of various heuristics that have been added to limit the number of
parameterized paths considered.  However, we are not going to find out
what needs to be adjusted until the code gets some real-world use, so
it's time to get it in there where it can be tested easily.

Note API change for index AM amcostestimate functions.  I'm not aware of
any non-core index AMs, but if there are any, they will need minor
adjustments.
This commit is contained in:
Tom Lane
2012-01-27 19:26:38 -05:00
parent b376ec6fa5
commit e2fa76d80b
26 changed files with 3884 additions and 2292 deletions

View File

@ -46,13 +46,28 @@ int geqo_threshold;
join_search_hook_type join_search_hook = NULL;
static void set_base_rel_sizes(PlannerInfo *root);
static void set_base_rel_pathlists(PlannerInfo *root);
static void set_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_foreign_size(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels,
List *all_child_pathkeys,
Relids required_outer);
static List *accumulate_append_subpath(List *subpaths, Path *path);
static void set_dummy_rel_pathlist(RelOptInfo *rel);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
@ -65,8 +80,6 @@ static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
bool *differentTypes);
@ -91,10 +104,33 @@ RelOptInfo *
make_one_rel(PlannerInfo *root, List *joinlist)
{
RelOptInfo *rel;
Index rti;
/*
* Construct the all_baserels Relids set.
*/
root->all_baserels = NULL;
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->simple_rel_array[rti];
/* there may be empty slots corresponding to non-baserel RTEs */
if (brel == NULL)
continue;
Assert(brel->relid == rti); /* sanity check on array */
/* ignore RTEs that are "other rels" */
if (brel->reloptkind != RELOPT_BASEREL)
continue;
root->all_baserels = bms_add_member(root->all_baserels, brel->relid);
}
/*
* Generate access paths for the base rels.
*/
set_base_rel_sizes(root);
set_base_rel_pathlists(root);
/*
@ -105,35 +141,41 @@ make_one_rel(PlannerInfo *root, List *joinlist)
/*
* The result should join all and only the query's base rels.
*/
#ifdef USE_ASSERT_CHECKING
{
int num_base_rels = 0;
Index rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;
Assert(brel->relid == rti); /* sanity check on array */
/* ignore RTEs that are "other rels" */
if (brel->reloptkind != RELOPT_BASEREL)
continue;
Assert(bms_is_member(rti, rel->relids));
num_base_rels++;
}
Assert(bms_num_members(rel->relids) == num_base_rels);
}
#endif
Assert(bms_equal(rel->relids, root->all_baserels));
return rel;
}
/*
* set_base_rel_sizes
* Set the size estimates (rows and widths) for each base-relation entry.
*
* We do this in a separate pass over the base rels so that rowcount
* estimates are available for parameterized path generation.
*/
static void
set_base_rel_sizes(PlannerInfo *root)
{
Index rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *rel = root->simple_rel_array[rti];
/* there may be empty slots corresponding to non-baserel RTEs */
if (rel == NULL)
continue;
Assert(rel->relid == rti); /* sanity check on array */
/* ignore RTEs that are "other rels" */
if (rel->reloptkind != RELOPT_BASEREL)
continue;
set_rel_size(root, rel, rti, root->simple_rte_array[rti]);
}
}
/*
* set_base_rel_pathlists
* Finds all paths available for scanning each base-relation entry.
@ -164,11 +206,11 @@ set_base_rel_pathlists(PlannerInfo *root)
}
/*
* set_rel_pathlist
* Build access paths for a base relation
* set_rel_size
* Set size estimates for a base relation
*/
static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
set_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
if (rel->reloptkind == RELOPT_BASEREL &&
@ -179,10 +221,78 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
* so set up a single dummy path for it. Here we only check this for
* regular baserels; if it's an otherrel, CE was already checked in
* set_append_rel_pathlist().
*
* In this case, we go ahead and set up the relation's path right away
* instead of leaving it for set_rel_pathlist to do. This is because
* we don't have a convention for marking a rel as dummy except by
* assigning a dummy path to it.
*/
set_dummy_rel_pathlist(rel);
}
else if (rte->inh)
{
/* It's an "append relation", process accordingly */
set_append_rel_size(root, rel, rti, rte);
}
else
{
switch (rel->rtekind)
{
case RTE_RELATION:
if (rte->relkind == RELKIND_FOREIGN_TABLE)
{
/* Foreign table */
set_foreign_size(root, rel, rte);
}
else
{
/* Plain relation */
set_plain_rel_size(root, rel, rte);
}
break;
case RTE_SUBQUERY:
/*
* Subqueries don't support parameterized paths, so just go
* ahead and build their paths immediately.
*/
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
set_function_size_estimates(root, rel);
break;
case RTE_VALUES:
set_values_size_estimates(root, rel);
break;
case RTE_CTE:
/*
* CTEs don't support parameterized paths, so just go ahead
* and build their paths immediately.
*/
if (rte->self_reference)
set_worktable_pathlist(root, rel, rte);
else
set_cte_pathlist(root, rel, rte);
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
}
}
}
/*
* set_rel_pathlist
* Build access paths for a base relation
*/
static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
if (IS_DUMMY_REL(rel))
{
/* We already proved the relation empty, so nothing more to do */
}
else if (rte->inh)
{
/* It's an "append relation", process accordingly */
set_append_rel_pathlist(root, rel, rti, rte);
@ -204,23 +314,18 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
}
break;
case RTE_SUBQUERY:
/* Subquery --- generate a separate plan for it */
set_subquery_pathlist(root, rel, rti, rte);
/* Subquery --- fully handled during set_rel_size */
break;
case RTE_FUNCTION:
/* RangeFunction --- generate a suitable path for it */
/* RangeFunction */
set_function_pathlist(root, rel, rte);
break;
case RTE_VALUES:
/* Values list --- generate a suitable path for it */
/* Values list */
set_values_pathlist(root, rel, rte);
break;
case RTE_CTE:
/* CTE reference --- generate a suitable path for it */
if (rte->self_reference)
set_worktable_pathlist(root, rel, rte);
else
set_cte_pathlist(root, rel, rte);
/* CTE reference --- fully handled during set_rel_size */
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
@ -234,11 +339,11 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
}
/*
* set_plain_rel_pathlist
* Build access paths for a plain relation (no subquery, no inheritance)
* set_plain_rel_size
* Set size estimates for a plain relation (no subquery, no inheritance)
*/
static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/*
* Test any partial indexes of rel for applicability. We must do this
@ -259,15 +364,15 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
check_partial_indexes(root, rel);
set_baserel_size_estimates(root, rel);
}
}
/*
* Generate paths and add them to the rel's pathlist.
*
* Note: add_path() will discard any paths that are dominated by another
* available path, keeping only those paths that are superior along at
* least one dimension of cost or sortedness.
*/
/*
* set_plain_rel_pathlist
* Build access paths for a plain relation (no subquery, no inheritance)
*/
static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Consider sequential scan */
add_path(rel, create_seqscan_path(root, rel));
@ -282,8 +387,33 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
}
/*
* set_append_rel_pathlist
* Build access paths for an "append relation"
* set_foreign_size
* Set size estimates for a foreign table RTE
*/
static void
set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_foreign_size_estimates(root, rel);
}
/*
* set_foreign_pathlist
* Build the (single) access path for a foreign table RTE
*/
static void
set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Generate appropriate path */
add_path(rel, (Path *) create_foreignscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* set_append_rel_size
* Set size estimates for an "append relation"
*
* The passed-in rel and RTE represent the entire append relation. The
* relation's contents are computed by appending together the output of
@ -293,13 +423,10 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
* a good thing because their outputs are not the same size.
*/
static void
set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
List *live_childrels = NIL;
List *subpaths = NIL;
List *all_child_pathkeys = NIL;
double parent_rows;
double parent_size;
double *parent_attrsizes;
@ -325,10 +452,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
nattrs = rel->max_attr - rel->min_attr + 1;
parent_attrsizes = (double *) palloc0(nattrs * sizeof(double));
/*
* Generate access paths for each member relation, and pick the cheapest
* path for each one.
*/
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
@ -337,7 +460,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *childrel;
List *childquals;
Node *childqual;
ListCell *lcp;
ListCell *parentvars;
ListCell *childvars;
@ -394,8 +516,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
{
/*
* This child need not be scanned, so we can omit it from the
* appendrel. Mark it with a dummy cheapest-path though, in case
* best_appendrel_indexscan() looks at it later.
* appendrel.
*/
set_dummy_rel_pathlist(childrel);
continue;
@ -438,65 +559,20 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
*/
/*
* Compute the child's access paths.
* Compute the child's size.
*/
set_rel_pathlist(root, childrel, childRTindex, childRTE);
set_rel_size(root, childrel, childRTindex, childRTE);
/*
* It is possible that constraint exclusion detected a contradiction
* within a child subquery, even though we didn't prove one above.
* If what we got back was a dummy path, we can skip this child.
* If so, we can skip this child.
*/
if (IS_DUMMY_PATH(childrel->cheapest_total_path))
if (IS_DUMMY_REL(childrel))
continue;
/*
* Child is live, so add its cheapest access path to the Append path
* we are constructing for the parent.
*/
subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path);
/* Remember which childrels are live, for MergeAppend logic below */
live_childrels = lappend(live_childrels, childrel);
/*
* Collect a list of all the available path orderings for all the
* children. We use this as a heuristic to indicate which sort
* orderings we should build MergeAppend paths for.
*/
foreach(lcp, childrel->pathlist)
{
Path *childpath = (Path *) lfirst(lcp);
List *childkeys = childpath->pathkeys;
ListCell *lpk;
bool found = false;
/* Ignore unsorted paths */
if (childkeys == NIL)
continue;
/* Have we already seen this ordering? */
foreach(lpk, all_child_pathkeys)
{
List *existing_pathkeys = (List *) lfirst(lpk);
if (compare_pathkeys(existing_pathkeys,
childkeys) == PATHKEYS_EQUAL)
{
found = true;
break;
}
}
if (!found)
{
/* No, so add it to all_child_pathkeys */
all_child_pathkeys = lappend(all_child_pathkeys, childkeys);
}
}
/*
* Accumulate size information from each child.
* Accumulate size information from each live child.
*/
if (childrel->rows > 0)
{
@ -560,24 +636,208 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
rel->tuples = parent_rows;
pfree(parent_attrsizes);
}
/*
* set_append_rel_pathlist
* Build access paths for an "append relation"
*/
static void
set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
List *live_childrels = NIL;
List *subpaths = NIL;
List *all_child_pathkeys = NIL;
List *all_child_outers = NIL;
ListCell *l;
/*
* Next, build an unordered Append path for the rel. (Note: this is
* correct even if we have zero or one live subpath due to constraint
* exclusion.)
* Generate access paths for each member relation, and remember the
* cheapest path for each one. Also, identify all pathkeys (orderings)
* and parameterizations (required_outer sets) available for the member
* relations.
*/
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
ListCell *lcp;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
/* Re-locate the child RTE and RelOptInfo */
childRTindex = appinfo->child_relid;
childRTE = root->simple_rte_array[childRTindex];
childrel = root->simple_rel_array[childRTindex];
/*
* Compute the child's access paths.
*/
set_rel_pathlist(root, childrel, childRTindex, childRTE);
/*
* If child is dummy, ignore it.
*/
if (IS_DUMMY_REL(childrel))
continue;
/*
* Child is live, so add its cheapest access path to the Append path
* we are constructing for the parent.
*/
subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path);
/* Remember which childrels are live, for logic below */
live_childrels = lappend(live_childrels, childrel);
/*
* Collect lists of all the available path orderings and
* parameterizations for all the children. We use these as a
* heuristic to indicate which sort orderings and parameterizations we
* should build Append and MergeAppend paths for.
*/
foreach(lcp, childrel->pathlist)
{
Path *childpath = (Path *) lfirst(lcp);
List *childkeys = childpath->pathkeys;
Relids childouter = childpath->required_outer;
/* Unsorted paths don't contribute to pathkey list */
if (childkeys != NIL)
{
ListCell *lpk;
bool found = false;
/* Have we already seen this ordering? */
foreach(lpk, all_child_pathkeys)
{
List *existing_pathkeys = (List *) lfirst(lpk);
if (compare_pathkeys(existing_pathkeys,
childkeys) == PATHKEYS_EQUAL)
{
found = true;
break;
}
}
if (!found)
{
/* No, so add it to all_child_pathkeys */
all_child_pathkeys = lappend(all_child_pathkeys,
childkeys);
}
}
/* Unparameterized paths don't contribute to param-set list */
if (childouter)
{
ListCell *lco;
bool found = false;
/* Have we already seen this param set? */
foreach(lco, all_child_outers)
{
Relids existing_outers = (Relids) lfirst(lco);
if (bms_equal(existing_outers, childouter))
{
found = true;
break;
}
}
if (!found)
{
/* No, so add it to all_child_outers */
all_child_outers = lappend(all_child_outers,
childouter);
}
}
}
}
/*
* Next, build an unordered, unparameterized Append path for the rel.
* (Note: this is correct even if we have zero or one live subpath due to
* constraint exclusion.)
*/
add_path(rel, (Path *) create_append_path(rel, subpaths));
/*
* Next, build MergeAppend paths based on the collected list of child
* pathkeys. We consider both cheapest-startup and cheapest-total cases,
* ie, for each interesting ordering, collect all the cheapest startup
* subpaths and all the cheapest total paths, and build a MergeAppend path
* for each list.
* Build unparameterized MergeAppend paths based on the collected list of
* child pathkeys.
*/
foreach(l, all_child_pathkeys)
generate_mergeappend_paths(root, rel, live_childrels,
all_child_pathkeys, NULL);
/*
* Build Append and MergeAppend paths for each parameterization seen
* among the child rels. (This may look pretty expensive, but in most
* cases of practical interest, the child relations will tend to expose
* the same parameterizations and pathkeys, so that not that many cases
* actually get considered here.)
*/
foreach(l, all_child_outers)
{
List *pathkeys = (List *) lfirst(l);
Relids required_outer = (Relids) lfirst(l);
ListCell *lcr;
/* Select the child paths for an Append with this parameterization */
subpaths = NIL;
foreach(lcr, live_childrels)
{
RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr);
Path *cheapest_total;
cheapest_total =
get_cheapest_path_for_pathkeys(childrel->pathlist,
NIL,
required_outer,
TOTAL_COST);
Assert(cheapest_total != NULL);
subpaths = accumulate_append_subpath(subpaths, cheapest_total);
}
add_path(rel, (Path *) create_append_path(rel, subpaths));
/* And build parameterized MergeAppend paths */
generate_mergeappend_paths(root, rel, live_childrels,
all_child_pathkeys, required_outer);
}
/* Select cheapest paths */
set_cheapest(rel);
}
/*
* generate_mergeappend_paths
* Generate MergeAppend paths for an append relation
*
* Generate a path for each ordering (pathkey list) appearing in
* all_child_pathkeys. If required_outer isn't NULL, accept paths having
* those relations as required outer relations.
*
* We consider both cheapest-startup and cheapest-total cases, ie, for each
* interesting ordering, collect all the cheapest startup subpaths and all the
* cheapest total paths, and build a MergeAppend path for each case.
*/
static void
generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels,
List *all_child_pathkeys,
Relids required_outer)
{
ListCell *lcp;
foreach(lcp, all_child_pathkeys)
{
List *pathkeys = (List *) lfirst(lcp);
List *startup_subpaths = NIL;
List *total_subpaths = NIL;
bool startup_neq_total = false;
@ -594,20 +854,32 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
cheapest_startup =
get_cheapest_path_for_pathkeys(childrel->pathlist,
pathkeys,
required_outer,
STARTUP_COST);
cheapest_total =
get_cheapest_path_for_pathkeys(childrel->pathlist,
pathkeys,
required_outer,
TOTAL_COST);
/*
* If we can't find any paths with the right order just add the
* cheapest-total path; we'll have to sort it.
* If we can't find any paths with the right order just use the
* cheapest-total path; we'll have to sort it later. We can
* use the cheapest path for the parameterization, though.
*/
if (cheapest_startup == NULL)
cheapest_startup = childrel->cheapest_total_path;
if (cheapest_total == NULL)
cheapest_total = childrel->cheapest_total_path;
if (cheapest_startup == NULL || cheapest_total == NULL)
{
if (required_outer)
cheapest_startup = cheapest_total =
get_cheapest_path_for_pathkeys(childrel->pathlist,
NIL,
required_outer,
TOTAL_COST);
else
cheapest_startup = cheapest_total =
childrel->cheapest_total_path;
Assert(cheapest_total != NULL);
}
/*
* Notice whether we actually have different paths for the
@ -634,9 +906,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
total_subpaths,
pathkeys));
}
/* Select cheapest path */
set_cheapest(rel);
}
/*
@ -667,7 +936,7 @@ accumulate_append_subpath(List *subpaths, Path *path)
* Build a dummy path for a relation that's been excluded by constraints
*
* Rather than inventing a special "dummy" path type, we represent this as an
* AppendPath with no members (see also IS_DUMMY_PATH macro).
* AppendPath with no members (see also IS_DUMMY_PATH/IS_DUMMY_REL macros).
*/
static void
set_dummy_rel_pathlist(RelOptInfo *rel)
@ -676,6 +945,9 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
rel->rows = 0;
rel->width = 0;
/* Discard any pre-existing paths; no further need for them */
rel->pathlist = NIL;
add_path(rel, (Path *) create_append_path(rel, NIL));
/* Select cheapest path (pretty easy in this case...) */
@ -707,6 +979,9 @@ has_multiple_baserels(PlannerInfo *root)
/*
* set_subquery_pathlist
* Build the (single) access path for a subquery RTE
*
* There's no need for a separate set_subquery_size phase, since we don't
* support parameterized paths for subqueries.
*/
static void
set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
@ -847,9 +1122,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
static void
set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_function_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, create_functionscan_path(root, rel));
@ -864,9 +1136,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
static void
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_values_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel));
@ -877,6 +1146,9 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/*
* set_cte_pathlist
* Build the (single) access path for a non-self-reference CTE RTE
*
* There's no need for a separate set_cte_size phase, since we don't
* support parameterized paths for CTEs.
*/
static void
set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
@ -935,6 +1207,9 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/*
* set_worktable_pathlist
* Build the (single) access path for a self-reference CTE RTE
*
* There's no need for a separate set_worktable_size phase, since we don't
* support parameterized paths for CTEs.
*/
static void
set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
@ -973,23 +1248,6 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
set_cheapest(rel);
}
/*
* set_foreign_pathlist
* Build the (single) access path for a foreign table RTE
*/
static void
set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_foreign_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, (Path *) create_foreignscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* make_rel_from_joinlist
* Build access paths using a "joinlist" to guide the join path search.