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:
@ -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.
|
||||
|
Reference in New Issue
Block a user