#include "nodes/relation.h"

Go to the source code of this file.
| void add_path | ( | RelOptInfo * | parent_rel, | |
| Path * | new_path | |||
| ) |
Definition at line 395 of file pathnode.c.
References BMS_EQUAL, BMS_SUBSET1, BMS_SUBSET2, bms_subset_compare(), CHECK_FOR_INTERRUPTS, compare_path_costs_fuzzily(), compare_pathkeys(), RelOptInfo::consider_startup, COSTS_BETTER1, COSTS_BETTER2, COSTS_DIFFERENT, COSTS_EQUAL, IsA, lappend_cell(), lcons(), lfirst, list_delete_cell(), list_head(), lnext, NIL, NULL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER1, PATHKEYS_BETTER2, PATHKEYS_DIFFERENT, RelOptInfo::pathlist, pfree(), Path::rows, and Path::total_cost.
Referenced by create_index_paths(), create_tidscan_paths(), fileGetForeignPaths(), generate_mergeappend_paths(), get_index_paths(), mark_dummy_rel(), postgresGetForeignPaths(), set_append_rel_pathlist(), set_cte_pathlist(), set_dummy_rel_pathlist(), set_function_pathlist(), set_plain_rel_pathlist(), set_subquery_pathlist(), set_values_pathlist(), set_worktable_pathlist(), try_hashjoin_path(), try_mergejoin_path(), and try_nestloop_path().
{
bool accept_new = true; /* unless we find a superior old path */
ListCell *insert_after = NULL; /* where to insert new item */
List *new_path_pathkeys;
ListCell *p1;
ListCell *p1_prev;
ListCell *p1_next;
/*
* This is a convenient place to check for query cancel --- no part of the
* planner goes very long without calling add_path().
*/
CHECK_FOR_INTERRUPTS();
/* Pretend parameterized paths have no pathkeys, per comment above */
new_path_pathkeys = new_path->param_info ? NIL : new_path->pathkeys;
/*
* Loop to check proposed new path against old paths. Note it is possible
* for more than one old path to be tossed out because new_path dominates
* it.
*
* We can't use foreach here because the loop body may delete the current
* list cell.
*/
p1_prev = NULL;
for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next)
{
Path *old_path = (Path *) lfirst(p1);
bool remove_old = false; /* unless new proves superior */
PathCostComparison costcmp;
PathKeysComparison keyscmp;
BMS_Comparison outercmp;
p1_next = lnext(p1);
/*
* Do a fuzzy cost comparison with 1% fuzziness limit. (XXX does this
* percentage need to be user-configurable?)
*/
costcmp = compare_path_costs_fuzzily(new_path, old_path, 1.01,
parent_rel->consider_startup);
/*
* If the two paths compare differently for startup and total cost,
* then we want to keep both, and we can skip comparing pathkeys and
* required_outer rels. If they compare the same, proceed with the
* other comparisons. Row count is checked last. (We make the tests
* in this order because the cost comparison is most likely to turn
* out "different", and the pathkeys comparison next most likely. As
* explained above, row count very seldom makes a difference, so even
* though it's cheap to compare there's not much point in checking it
* earlier.)
*/
if (costcmp != COSTS_DIFFERENT)
{
/* Similarly check to see if either dominates on pathkeys */
List *old_path_pathkeys;
old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
keyscmp = compare_pathkeys(new_path_pathkeys,
old_path_pathkeys);
if (keyscmp != PATHKEYS_DIFFERENT)
{
switch (costcmp)
{
case COSTS_EQUAL:
outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
PATH_REQ_OUTER(old_path));
if (keyscmp == PATHKEYS_BETTER1)
{
if ((outercmp == BMS_EQUAL ||
outercmp == BMS_SUBSET1) &&
new_path->rows <= old_path->rows)
remove_old = true; /* new dominates old */
}
else if (keyscmp == PATHKEYS_BETTER2)
{
if ((outercmp == BMS_EQUAL ||
outercmp == BMS_SUBSET2) &&
new_path->rows >= old_path->rows)
accept_new = false; /* old dominates new */
}
else /* keyscmp == PATHKEYS_EQUAL */
{
if (outercmp == BMS_EQUAL)
{
/*
* Same pathkeys and outer rels, and fuzzily
* the same cost, so keep just one; to decide
* which, first check rows and then do a fuzzy
* cost comparison with very small fuzz limit.
* (We used to do an exact cost comparison,
* but that results in annoying
* platform-specific plan variations due to
* roundoff in the cost estimates.) If things
* are still tied, arbitrarily keep only the
* old path. Notice that we will keep only
* the old path even if the less-fuzzy
* comparison decides the startup and total
* costs compare differently.
*/
if (new_path->rows < old_path->rows)
remove_old = true; /* new dominates old */
else if (new_path->rows > old_path->rows)
accept_new = false; /* old dominates new */
else if (compare_path_costs_fuzzily(new_path,
old_path,
1.0000000001,
parent_rel->consider_startup) == COSTS_BETTER1)
remove_old = true; /* new dominates old */
else
accept_new = false; /* old equals or
* dominates new */
}
else if (outercmp == BMS_SUBSET1 &&
new_path->rows <= old_path->rows)
remove_old = true; /* new dominates old */
else if (outercmp == BMS_SUBSET2 &&
new_path->rows >= old_path->rows)
accept_new = false; /* old dominates new */
/* else different parameterizations, keep both */
}
break;
case COSTS_BETTER1:
if (keyscmp != PATHKEYS_BETTER2)
{
outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
PATH_REQ_OUTER(old_path));
if ((outercmp == BMS_EQUAL ||
outercmp == BMS_SUBSET1) &&
new_path->rows <= old_path->rows)
remove_old = true; /* new dominates old */
}
break;
case COSTS_BETTER2:
if (keyscmp != PATHKEYS_BETTER1)
{
outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
PATH_REQ_OUTER(old_path));
if ((outercmp == BMS_EQUAL ||
outercmp == BMS_SUBSET2) &&
new_path->rows >= old_path->rows)
accept_new = false; /* old dominates new */
}
break;
case COSTS_DIFFERENT:
/*
* can't get here, but keep this case to keep compiler
* quiet
*/
break;
}
}
}
/*
* Remove current element from pathlist if dominated by new.
*/
if (remove_old)
{
parent_rel->pathlist = list_delete_cell(parent_rel->pathlist,
p1, p1_prev);
/*
* Delete the data pointed-to by the deleted cell, if possible
*/
if (!IsA(old_path, IndexPath))
pfree(old_path);
/* p1_prev does not advance */
}
else
{
/* new belongs after this old path if it has cost >= old's */
if (new_path->total_cost >= old_path->total_cost)
insert_after = p1;
/* p1_prev advances */
p1_prev = p1;
}
/*
* If we found an old path that dominates new_path, we can quit
* scanning the pathlist; we will not add new_path, and we assume
* new_path cannot dominate any other elements of the pathlist.
*/
if (!accept_new)
break;
}
if (accept_new)
{
/* Accept the new path: insert it at proper place in pathlist */
if (insert_after)
lappend_cell(parent_rel->pathlist, insert_after, new_path);
else
parent_rel->pathlist = lcons(new_path, parent_rel->pathlist);
}
else
{
/* Reject and recycle the new path */
if (!IsA(new_path, IndexPath))
pfree(new_path);
}
}
| bool add_path_precheck | ( | RelOptInfo * | parent_rel, | |
| Cost | startup_cost, | |||
| Cost | total_cost, | |||
| List * | pathkeys, | |||
| Relids | required_outer | |||
| ) |
Definition at line 620 of file pathnode.c.
References bms_equal(), compare_pathkeys(), lfirst, NIL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER2, PATHKEYS_EQUAL, RelOptInfo::pathlist, Path::startup_cost, and Path::total_cost.
Referenced by try_hashjoin_path(), try_mergejoin_path(), and try_nestloop_path().
{
List *new_path_pathkeys;
ListCell *p1;
/* Pretend parameterized paths have no pathkeys, per add_path policy */
new_path_pathkeys = required_outer ? NIL : pathkeys;
foreach(p1, parent_rel->pathlist)
{
Path *old_path = (Path *) lfirst(p1);
PathKeysComparison keyscmp;
/*
* We are looking for an old_path with the same parameterization (and
* by assumption the same rowcount) that dominates the new path on
* pathkeys as well as both cost metrics. If we find one, we can
* reject the new path.
*
* For speed, we make exact rather than fuzzy cost comparisons. If an
* old path dominates the new path exactly on both costs, it will
* surely do so fuzzily.
*/
if (total_cost >= old_path->total_cost)
{
/* can win on startup cost only if unparameterized */
if (startup_cost >= old_path->startup_cost || required_outer)
{
/* new path does not win on cost, so check pathkeys... */
List *old_path_pathkeys;
old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
keyscmp = compare_pathkeys(new_path_pathkeys,
old_path_pathkeys);
if (keyscmp == PATHKEYS_EQUAL ||
keyscmp == PATHKEYS_BETTER2)
{
/* new path does not win on pathkeys... */
if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
{
/* Found an old path that dominates the new one */
return false;
}
}
}
}
else
{
/*
* Since the pathlist is sorted by total_cost, we can stop looking
* once we reach a path with a total_cost larger than the new
* path's.
*/
break;
}
}
return true;
}
| RelOptInfo* build_join_rel | ( | PlannerInfo * | root, | |
| Relids | joinrelids, | |||
| RelOptInfo * | outer_rel, | |||
| RelOptInfo * | inner_rel, | |||
| SpecialJoinInfo * | sjinfo, | |||
| List ** | restrictlist_ptr | |||
| ) |
Definition at line 323 of file relnode.c.
References add_placeholders_to_joinrel(), RelOptInfo::allvisfrac, Assert, RelOptInfo::attr_needed, RelOptInfo::attr_widths, RelOptInfo::baserestrictcost, RelOptInfo::baserestrictinfo, bms_copy(), bms_num_members(), build_joinrel_joinlist(), build_joinrel_restrictlist(), build_joinrel_tlist(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_startup_path, RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, RelOptInfo::consider_startup, RelOptInfo::fdw_private, RelOptInfo::fdwroutine, find_join_rel(), RelOptInfo::has_eclass_joins, has_relevant_eclass_joinclause(), hash_search(), RelOptInfo::indexlist, PlannerInfo::join_cur_level, JoinHashEntry::join_rel, PlannerInfo::join_rel_hash, PlannerInfo::join_rel_level, PlannerInfo::join_rel_list, RelOptInfo::joininfo, lappend(), RelOptInfo::lateral_relids, RelOptInfo::lateral_vars, makeNode, RelOptInfo::max_attr, RelOptInfo::min_attr, RelOptInfo::pages, RelOptInfo::pathlist, QualCost::per_tuple, RelOptInfo::ppilist, RelOptInfo::relid, RelOptInfo::relids, RelOptInfo::reloptkind, RelOptInfo::reltargetlist, RelOptInfo::rows, RelOptInfo::rtekind, set_joinrel_size_estimates(), QualCost::startup, RelOptInfo::subplan, RelOptInfo::subplan_params, RelOptInfo::subroot, PlannerInfo::tuple_fraction, RelOptInfo::tuples, and RelOptInfo::width.
Referenced by make_join_rel().
{
RelOptInfo *joinrel;
List *restrictlist;
/*
* See if we already have a joinrel for this set of base rels.
*/
joinrel = find_join_rel(root, joinrelids);
if (joinrel)
{
/*
* Yes, so we only need to figure the restrictlist for this particular
* pair of component relations.
*/
if (restrictlist_ptr)
*restrictlist_ptr = build_joinrel_restrictlist(root,
joinrel,
outer_rel,
inner_rel);
return joinrel;
}
/*
* Nope, so make one.
*/
joinrel = makeNode(RelOptInfo);
joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = bms_copy(joinrelids);
joinrel->rows = 0;
joinrel->width = 0;
/* cheap startup cost is interesting iff not all tuples to be retrieved */
joinrel->consider_startup = (root->tuple_fraction > 0);
joinrel->reltargetlist = NIL;
joinrel->pathlist = NIL;
joinrel->ppilist = NIL;
joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
joinrel->cheapest_unique_path = NULL;
joinrel->cheapest_parameterized_paths = NIL;
joinrel->relid = 0; /* indicates not a baserel */
joinrel->rtekind = RTE_JOIN;
joinrel->min_attr = 0;
joinrel->max_attr = 0;
joinrel->attr_needed = NULL;
joinrel->attr_widths = NULL;
joinrel->lateral_vars = NIL;
joinrel->lateral_relids = NULL;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->allvisfrac = 0;
joinrel->subplan = NULL;
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost.startup = 0;
joinrel->baserestrictcost.per_tuple = 0;
joinrel->joininfo = NIL;
joinrel->has_eclass_joins = false;
/*
* 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).
*
* NOTE: the tlist order for a join rel will depend on which pair of outer
* and inner rels we first try to build it from. But the contents should
* be the same regardless.
*/
build_joinrel_tlist(root, joinrel, outer_rel);
build_joinrel_tlist(root, joinrel, inner_rel);
add_placeholders_to_joinrel(root, joinrel);
/*
* Construct restrict and join clause lists for the new joinrel. (The
* caller might or might not need the restrictlist, but I need it anyway
* for set_joinrel_size_estimates().)
*/
restrictlist = build_joinrel_restrictlist(root, joinrel,
outer_rel, inner_rel);
if (restrictlist_ptr)
*restrictlist_ptr = restrictlist;
build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
/*
* This is also the right place to check whether the joinrel has any
* pending EquivalenceClass joins.
*/
joinrel->has_eclass_joins = has_relevant_eclass_joinclause(root, joinrel);
/*
* Set estimates of the joinrel's size.
*/
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
sjinfo, restrictlist);
/*
* 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
* the new joinrel to the end of the list!
*/
root->join_rel_list = lappend(root->join_rel_list, joinrel);
if (root->join_rel_hash)
{
JoinHashEntry *hentry;
bool found;
hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
&(joinrel->relids),
HASH_ENTER,
&found);
Assert(!found);
hentry->join_rel = joinrel;
}
/*
* Also, if dynamic-programming join search is active, add the new joinrel
* to the appropriate sublist. Note: you might think the Assert on number
* of members should be for equality, but some of the level 1 rels might
* have been joinrels already, so we can only assert <=.
*/
if (root->join_rel_level)
{
Assert(root->join_cur_level > 0);
Assert(root->join_cur_level <= bms_num_members(joinrel->relids));
root->join_rel_level[root->join_cur_level] =
lappend(root->join_rel_level[root->join_cur_level], joinrel);
}
return joinrel;
}
| RelOptInfo* build_simple_rel | ( | PlannerInfo * | root, | |
| int | relid, | |||
| RelOptKind | reloptkind | |||
| ) |
Definition at line 83 of file relnode.c.
References RelOptInfo::allvisfrac, PlannerInfo::append_rel_list, Assert, RelOptInfo::attr_needed, RelOptInfo::attr_widths, RelOptInfo::baserestrictcost, RelOptInfo::baserestrictinfo, bms_make_singleton(), build_simple_rel(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_startup_path, RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, AppendRelInfo::child_relid, Alias::colnames, RelOptInfo::consider_startup, elog, RangeTblEntry::eref, ERROR, RelOptInfo::fdw_private, RelOptInfo::fdwroutine, get_relation_info(), RelOptInfo::has_eclass_joins, RelOptInfo::indexlist, RangeTblEntry::inh, RelOptInfo::joininfo, RelOptInfo::lateral_relids, RelOptInfo::lateral_vars, lfirst, list_length(), makeNode, RelOptInfo::max_attr, RelOptInfo::min_attr, NULL, RelOptInfo::pages, palloc0(), AppendRelInfo::parent_relid, RelOptInfo::pathlist, QualCost::per_tuple, RelOptInfo::ppilist, RangeTblEntry::relid, RelOptInfo::relid, RelOptInfo::relids, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, RelOptInfo::reltargetlist, RelOptInfo::rows, RTE_CTE, RTE_FUNCTION, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RelOptInfo::rtekind, PlannerInfo::simple_rel_array, PlannerInfo::simple_rte_array, QualCost::startup, RelOptInfo::subplan, RelOptInfo::subplan_params, RelOptInfo::subroot, PlannerInfo::tuple_fraction, RelOptInfo::tuples, and RelOptInfo::width.
Referenced by add_base_rels_to_query(), build_simple_rel(), plan_cluster_use_sort(), and recurse_set_operations().
{
RelOptInfo *rel;
RangeTblEntry *rte;
/* Rel should not exist already */
Assert(relid > 0 && relid < root->simple_rel_array_size);
if (root->simple_rel_array[relid] != NULL)
elog(ERROR, "rel %d already exists", relid);
/* Fetch RTE for relation */
rte = root->simple_rte_array[relid];
Assert(rte != NULL);
rel = makeNode(RelOptInfo);
rel->reloptkind = reloptkind;
rel->relids = bms_make_singleton(relid);
rel->rows = 0;
rel->width = 0;
/* cheap startup cost is interesting iff not all tuples to be retrieved */
rel->consider_startup = (root->tuple_fraction > 0);
rel->reltargetlist = NIL;
rel->pathlist = NIL;
rel->ppilist = NIL;
rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
rel->cheapest_parameterized_paths = NIL;
rel->relid = relid;
rel->rtekind = rte->rtekind;
/* min_attr, max_attr, attr_needed, attr_widths are set below */
rel->lateral_vars = NIL;
rel->lateral_relids = NULL;
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
rel->allvisfrac = 0;
rel->subplan = NULL;
rel->subroot = NULL;
rel->subplan_params = NIL;
rel->fdwroutine = NULL;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
rel->joininfo = NIL;
rel->has_eclass_joins = false;
/* Check type of rtable entry */
switch (rte->rtekind)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
get_relation_info(root, rte->relid, rte->inh, rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
case RTE_VALUES:
case RTE_CTE:
/*
* Subquery, function, or values list --- set up attr range and
* arrays
*
* Note: 0 is included in range to support whole-row Vars
*/
rel->min_attr = 0;
rel->max_attr = list_length(rte->eref->colnames);
rel->attr_needed = (Relids *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
rel->attr_widths = (int32 *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));
break;
default:
elog(ERROR, "unrecognized RTE kind: %d",
(int) rte->rtekind);
break;
}
/* Save the finished struct in the query's simple_rel_array */
root->simple_rel_array[relid] = rel;
/*
* If this rel is an appendrel parent, recurse to build "other rel"
* RelOptInfos for its children. They are "other rels" because they are
* not in the main join tree, but we will need RelOptInfos to plan access
* to them.
*/
if (rte->inh)
{
ListCell *l;
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != relid)
continue;
(void) build_simple_rel(root, appinfo->child_relid,
RELOPT_OTHER_MEMBER_REL);
}
}
return rel;
}
Definition at line 1746 of file pathnode.c.
References Assert, bms_copy(), bms_del_members(), bms_free(), bms_is_empty(), bms_overlap(), bms_union(), Path::parent, PATH_REQ_OUTER, and RelOptInfo::relids.
Referenced by try_nestloop_path().
{
Relids outer_paramrels = PATH_REQ_OUTER(outer_path);
Relids inner_paramrels = PATH_REQ_OUTER(inner_path);
Relids required_outer;
/* inner_path can require rels from outer path, but not vice versa */
Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
/* easy case if inner path is not parameterized */
if (!inner_paramrels)
return bms_copy(outer_paramrels);
/* else, form the union ... */
required_outer = bms_union(outer_paramrels, inner_paramrels);
/* ... and remove any mention of now-satisfied outer rels */
required_outer = bms_del_members(required_outer,
outer_path->parent->relids);
/* maintain invariant that required_outer is exactly NULL if empty */
if (bms_is_empty(required_outer))
{
bms_free(required_outer);
required_outer = NULL;
}
return required_outer;
}
Definition at line 1778 of file pathnode.c.
References Assert, bms_overlap(), bms_union(), Path::parent, PATH_REQ_OUTER, and RelOptInfo::relids.
Referenced by try_hashjoin_path(), and try_mergejoin_path().
{
Relids outer_paramrels = PATH_REQ_OUTER(outer_path);
Relids inner_paramrels = PATH_REQ_OUTER(inner_path);
Relids required_outer;
/* neither path can require rels from the other */
Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
Assert(!bms_overlap(inner_paramrels, outer_path->parent->relids));
/* form the union ... */
required_outer = bms_union(outer_paramrels, inner_paramrels);
/* we do not need an explicit test for empty; bms_union gets it right */
return required_outer;
}
Definition at line 101 of file pathnode.c.
References compare_path_costs(), Path::startup_cost, Path::total_cost, and TOTAL_COST.
Referenced by choose_hashed_distinct(), choose_hashed_grouping(), choose_hashed_setop(), get_cheapest_fractional_path_for_pathkeys(), and query_planner().
{
Cost cost1,
cost2;
if (fraction <= 0.0 || fraction >= 1.0)
return compare_path_costs(path1, path2, TOTAL_COST);
cost1 = path1->startup_cost +
fraction * (path1->total_cost - path1->startup_cost);
cost2 = path2->startup_cost +
fraction * (path2->total_cost - path2->startup_cost);
if (cost1 < cost2)
return -1;
if (cost1 > cost2)
return +1;
return 0;
}
| int compare_path_costs | ( | Path * | path1, | |
| Path * | path2, | |||
| CostSelector | criterion | |||
| ) |
Definition at line 55 of file pathnode.c.
References Path::startup_cost, STARTUP_COST, and Path::total_cost.
Referenced by compare_fractional_path_costs(), get_cheapest_path_for_pathkeys(), match_unsorted_outer(), and set_cheapest().
{
if (criterion == STARTUP_COST)
{
if (path1->startup_cost < path2->startup_cost)
return -1;
if (path1->startup_cost > path2->startup_cost)
return +1;
/*
* If paths have the same startup cost (not at all unlikely), order
* them by total cost.
*/
if (path1->total_cost < path2->total_cost)
return -1;
if (path1->total_cost > path2->total_cost)
return +1;
}
else
{
if (path1->total_cost < path2->total_cost)
return -1;
if (path1->total_cost > path2->total_cost)
return +1;
/*
* If paths have the same total cost, order them by startup cost.
*/
if (path1->startup_cost < path2->startup_cost)
return -1;
if (path1->startup_cost > path2->startup_cost)
return +1;
}
return 0;
}
| AppendPath* create_append_path | ( | RelOptInfo * | rel, | |
| List * | subpaths, | |||
| Relids | required_outer | |||
| ) |
Definition at line 890 of file pathnode.c.
References Assert, bms_equal(), get_appendrel_parampathinfo(), lfirst, list_head(), makeNode, Path::param_info, Path::parent, AppendPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, subpath(), AppendPath::subpaths, and Path::total_cost.
Referenced by mark_dummy_rel(), set_append_rel_pathlist(), and set_dummy_rel_pathlist().
{
AppendPath *pathnode = makeNode(AppendPath);
ListCell *l;
pathnode->path.pathtype = T_Append;
pathnode->path.parent = rel;
pathnode->path.param_info = get_appendrel_parampathinfo(rel,
required_outer);
pathnode->path.pathkeys = NIL; /* result is always considered
* unsorted */
pathnode->subpaths = subpaths;
/*
* We don't bother with inventing a cost_append(), but just do it here.
*
* Compute rows and costs as sums of subplan rows and costs. We charge
* nothing extra for the Append itself, which perhaps is too optimistic,
* but since it doesn't do any selection or projection, it is a pretty
* cheap node. If you change this, see also make_append().
*/
pathnode->path.rows = 0;
pathnode->path.startup_cost = 0;
pathnode->path.total_cost = 0;
foreach(l, subpaths)
{
Path *subpath = (Path *) lfirst(l);
pathnode->path.rows += subpath->rows;
if (l == list_head(subpaths)) /* first node? */
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost += subpath->total_cost;
/* All child paths must have same parameterization */
Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
}
return pathnode;
}
| BitmapAndPath* create_bitmap_and_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | bitmapquals | |||
| ) |
Definition at line 815 of file pathnode.c.
References BitmapAndPath::bitmapquals, cost_bitmap_and_node(), makeNode, Path::param_info, Path::parent, BitmapAndPath::path, Path::pathkeys, and Path::pathtype.
Referenced by choose_bitmap_and().
{
BitmapAndPath *pathnode = makeNode(BitmapAndPath);
pathnode->path.pathtype = T_BitmapAnd;
pathnode->path.parent = rel;
pathnode->path.param_info = NULL; /* not used in bitmap trees */
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapquals = bitmapquals;
/* this sets bitmapselectivity as well as the regular cost fields: */
cost_bitmap_and_node(pathnode, root);
return pathnode;
}
| BitmapHeapPath* create_bitmap_heap_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Path * | bitmapqual, | |||
| Relids | required_outer, | |||
| double | loop_count | |||
| ) |
Definition at line 787 of file pathnode.c.
References BitmapHeapPath::bitmapqual, cost_bitmap_heap_scan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, BitmapHeapPath::path, Path::pathkeys, and Path::pathtype.
Referenced by create_index_paths(), and reparameterize_path().
{
BitmapHeapPath *pathnode = makeNode(BitmapHeapPath);
pathnode->path.pathtype = T_BitmapHeapScan;
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapqual = bitmapqual;
cost_bitmap_heap_scan(&pathnode->path, root, rel,
pathnode->path.param_info,
bitmapqual, loop_count);
return pathnode;
}
| BitmapOrPath* create_bitmap_or_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | bitmapquals | |||
| ) |
Definition at line 839 of file pathnode.c.
References BitmapOrPath::bitmapquals, cost_bitmap_or_node(), makeNode, Path::param_info, Path::parent, BitmapOrPath::path, Path::pathkeys, and Path::pathtype.
Referenced by generate_bitmap_or_paths().
{
BitmapOrPath *pathnode = makeNode(BitmapOrPath);
pathnode->path.pathtype = T_BitmapOr;
pathnode->path.parent = rel;
pathnode->path.param_info = NULL; /* not used in bitmap trees */
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapquals = bitmapquals;
/* this sets bitmapselectivity as well as the regular cost fields: */
cost_bitmap_or_node(pathnode, root);
return pathnode;
}
| Path* create_ctescan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1668 of file pathnode.c.
References cost_ctescan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by set_cte_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_CteScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* XXX for now, result is always unordered */
cost_ctescan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| ForeignPath* create_foreignscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| double | rows, | |||
| Cost | startup_cost, | |||
| Cost | total_cost, | |||
| List * | pathkeys, | |||
| Relids | required_outer, | |||
| List * | fdw_private | |||
| ) |
Definition at line 1717 of file pathnode.c.
References ForeignPath::fdw_private, get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, ForeignPath::path, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, and Path::total_cost.
Referenced by fileGetForeignPaths(), and postgresGetForeignPaths().
{
ForeignPath *pathnode = makeNode(ForeignPath);
pathnode->path.pathtype = T_ForeignScan;
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->path.rows = rows;
pathnode->path.startup_cost = startup_cost;
pathnode->path.total_cost = total_cost;
pathnode->path.pathkeys = pathkeys;
pathnode->fdw_private = fdw_private;
return pathnode;
}
| Path* create_functionscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1624 of file pathnode.c.
References cost_functionscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by set_function_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_FunctionScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* for now, assume unordered result */
cost_functionscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| HashPath* create_hashjoin_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| JoinType | jointype, | |||
| JoinCostWorkspace * | workspace, | |||
| SpecialJoinInfo * | sjinfo, | |||
| SemiAntiJoinFactors * | semifactors, | |||
| Path * | outer_path, | |||
| Path * | inner_path, | |||
| List * | restrict_clauses, | |||
| Relids | required_outer, | |||
| List * | hashclauses | |||
| ) |
Definition at line 1952 of file pathnode.c.
References final_cost_hashjoin(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, JoinPath::joinrestrictinfo, JoinPath::jointype, HashPath::jpath, makeNode, JoinPath::outerjoinpath, Path::param_info, Path::parent, JoinPath::path, HashPath::path_hashclauses, Path::pathkeys, and Path::pathtype.
Referenced by try_hashjoin_path().
{
HashPath *pathnode = makeNode(HashPath);
pathnode->jpath.path.pathtype = T_HashJoin;
pathnode->jpath.path.parent = joinrel;
pathnode->jpath.path.param_info =
get_joinrel_parampathinfo(root,
joinrel,
outer_path,
inner_path,
sjinfo,
required_outer,
&restrict_clauses);
/*
* A hashjoin never has pathkeys, since its output ordering is
* unpredictable due to possible batching. XXX If the inner relation is
* small enough, we could instruct the executor that it must not batch,
* and then we could assume that the output inherits the outer relation's
* ordering, which might save a sort step. However there is considerable
* downside if our estimate of the inner relation size is badly off. For
* the moment we don't risk it. (Note also that if we wanted to take this
* seriously, joinpath.c would have to consider many more paths for the
* outer rel than it does now.)
*/
pathnode->jpath.path.pathkeys = NIL;
pathnode->jpath.jointype = jointype;
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->path_hashclauses = hashclauses;
/* final_cost_hashjoin will fill in pathnode->num_batches */
final_cost_hashjoin(root, pathnode, workspace, sjinfo, semifactors);
return pathnode;
}
| IndexPath* create_index_path | ( | PlannerInfo * | root, | |
| IndexOptInfo * | index, | |||
| List * | indexclauses, | |||
| List * | indexclausecols, | |||
| List * | indexorderbys, | |||
| List * | indexorderbycols, | |||
| List * | pathkeys, | |||
| ScanDirection | indexscandir, | |||
| bool | indexonly, | |||
| Relids | required_outer, | |||
| double | loop_count | |||
| ) |
Definition at line 733 of file pathnode.c.
References cost_index(), expand_indexqual_conditions(), get_baserel_parampathinfo(), IndexPath::indexclauses, IndexPath::indexinfo, IndexPath::indexorderbycols, IndexPath::indexorderbys, IndexPath::indexqualcols, IndexPath::indexquals, IndexPath::indexscandir, makeNode, Path::param_info, Path::parent, IndexPath::path, Path::pathkeys, Path::pathtype, IndexOptInfo::rel, and T_IndexOnlyScan.
Referenced by build_index_paths(), and plan_cluster_use_sort().
{
IndexPath *pathnode = makeNode(IndexPath);
RelOptInfo *rel = index->rel;
List *indexquals,
*indexqualcols;
pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->path.pathkeys = pathkeys;
/* Convert clauses to indexquals the executor can handle */
expand_indexqual_conditions(index, indexclauses, indexclausecols,
&indexquals, &indexqualcols);
/* Fill in the pathnode */
pathnode->indexinfo = index;
pathnode->indexclauses = indexclauses;
pathnode->indexquals = indexquals;
pathnode->indexqualcols = indexqualcols;
pathnode->indexorderbys = indexorderbys;
pathnode->indexorderbycols = indexorderbycols;
pathnode->indexscandir = indexscandir;
cost_index(pathnode, root, loop_count);
return pathnode;
}
| MaterialPath* create_material_path | ( | RelOptInfo * | rel, | |
| Path * | subpath | |||
| ) |
Definition at line 1050 of file pathnode.c.
References Assert, cost_material(), makeNode, Path::param_info, Path::parent, MaterialPath::path, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, MaterialPath::subpath, Path::total_cost, and RelOptInfo::width.
Referenced by match_unsorted_outer().
{
MaterialPath *pathnode = makeNode(MaterialPath);
Assert(subpath->parent == rel);
pathnode->path.pathtype = T_Material;
pathnode->path.parent = rel;
pathnode->path.param_info = subpath->param_info;
pathnode->path.pathkeys = subpath->pathkeys;
pathnode->subpath = subpath;
cost_material(&pathnode->path,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
rel->width);
return pathnode;
}
| MergeAppendPath* create_merge_append_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | subpaths, | |||
| List * | pathkeys, | |||
| Relids | required_outer | |||
| ) |
Definition at line 937 of file pathnode.c.
References PlannerInfo::all_baserels, Assert, bms_equal(), cost_merge_append(), cost_sort(), get_appendrel_parampathinfo(), lfirst, PlannerInfo::limit_tuples, MergeAppendPath::limit_tuples, list_length(), makeNode, Path::param_info, Path::parent, MergeAppendPath::path, PATH_REQ_OUTER, Path::pathkeys, pathkeys_contained_in(), Path::pathtype, RelOptInfo::relids, Path::rows, Path::startup_cost, subpath(), MergeAppendPath::subpaths, Path::total_cost, RelOptInfo::tuples, RelOptInfo::width, and work_mem.
Referenced by generate_mergeappend_paths().
{
MergeAppendPath *pathnode = makeNode(MergeAppendPath);
Cost input_startup_cost;
Cost input_total_cost;
ListCell *l;
pathnode->path.pathtype = T_MergeAppend;
pathnode->path.parent = rel;
pathnode->path.param_info = get_appendrel_parampathinfo(rel,
required_outer);
pathnode->path.pathkeys = pathkeys;
pathnode->subpaths = subpaths;
/*
* Apply query-wide LIMIT if known and path is for sole base relation.
* (Handling this at this low level is a bit klugy.)
*/
if (bms_equal(rel->relids, root->all_baserels))
pathnode->limit_tuples = root->limit_tuples;
else
pathnode->limit_tuples = -1.0;
/*
* Add up the sizes and costs of the input paths.
*/
pathnode->path.rows = 0;
input_startup_cost = 0;
input_total_cost = 0;
foreach(l, subpaths)
{
Path *subpath = (Path *) lfirst(l);
pathnode->path.rows += subpath->rows;
if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
{
/* Subpath is adequately ordered, we won't need to sort it */
input_startup_cost += subpath->startup_cost;
input_total_cost += subpath->total_cost;
}
else
{
/* We'll need to insert a Sort node, so include cost for that */
Path sort_path; /* dummy for result of cost_sort */
cost_sort(&sort_path,
root,
pathkeys,
subpath->total_cost,
subpath->parent->tuples,
subpath->parent->width,
0.0,
work_mem,
pathnode->limit_tuples);
input_startup_cost += sort_path.startup_cost;
input_total_cost += sort_path.total_cost;
}
/* All child paths must have same parameterization */
Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
}
/* Now we can compute total costs of the MergeAppend */
cost_merge_append(&pathnode->path, root,
pathkeys, list_length(subpaths),
input_startup_cost, input_total_cost,
rel->tuples);
return pathnode;
}
| MergePath* create_mergejoin_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| JoinType | jointype, | |||
| JoinCostWorkspace * | workspace, | |||
| SpecialJoinInfo * | sjinfo, | |||
| Path * | outer_path, | |||
| Path * | inner_path, | |||
| List * | restrict_clauses, | |||
| List * | pathkeys, | |||
| Relids | required_outer, | |||
| List * | mergeclauses, | |||
| List * | outersortkeys, | |||
| List * | innersortkeys | |||
| ) |
Definition at line 1894 of file pathnode.c.
References final_cost_mergejoin(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, MergePath::innersortkeys, JoinPath::joinrestrictinfo, JoinPath::jointype, MergePath::jpath, makeNode, JoinPath::outerjoinpath, MergePath::outersortkeys, Path::param_info, Path::parent, JoinPath::path, MergePath::path_mergeclauses, Path::pathkeys, and Path::pathtype.
Referenced by try_mergejoin_path().
{
MergePath *pathnode = makeNode(MergePath);
pathnode->jpath.path.pathtype = T_MergeJoin;
pathnode->jpath.path.parent = joinrel;
pathnode->jpath.path.param_info =
get_joinrel_parampathinfo(root,
joinrel,
outer_path,
inner_path,
sjinfo,
required_outer,
&restrict_clauses);
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->jpath.jointype = jointype;
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->path_mergeclauses = mergeclauses;
pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys;
/* pathnode->materialize_inner will be set by final_cost_mergejoin */
final_cost_mergejoin(root, pathnode, workspace, sjinfo);
return pathnode;
}
| NestPath* create_nestloop_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| JoinType | jointype, | |||
| JoinCostWorkspace * | workspace, | |||
| SpecialJoinInfo * | sjinfo, | |||
| SemiAntiJoinFactors * | semifactors, | |||
| Path * | outer_path, | |||
| Path * | inner_path, | |||
| List * | restrict_clauses, | |||
| List * | pathkeys, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1812 of file pathnode.c.
References bms_overlap(), bms_union(), final_cost_nestloop(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, join_clause_is_movable_into(), JoinPath::joinrestrictinfo, JoinPath::jointype, lappend(), lfirst, makeNode, JoinPath::outerjoinpath, Path::param_info, Path::parent, JoinPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, and RelOptInfo::relids.
Referenced by try_nestloop_path().
{
NestPath *pathnode = makeNode(NestPath);
Relids inner_req_outer = PATH_REQ_OUTER(inner_path);
/*
* If the inner path is parameterized by the outer, we must drop any
* restrict_clauses that are due to be moved into the inner path. We have
* to do this now, rather than postpone the work till createplan time,
* because the restrict_clauses list can affect the size and cost
* estimates for this path.
*/
if (bms_overlap(inner_req_outer, outer_path->parent->relids))
{
Relids inner_and_outer = bms_union(inner_path->parent->relids,
inner_req_outer);
List *jclauses = NIL;
ListCell *lc;
foreach(lc, restrict_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
if (!join_clause_is_movable_into(rinfo,
inner_path->parent->relids,
inner_and_outer))
jclauses = lappend(jclauses, rinfo);
}
restrict_clauses = jclauses;
}
pathnode->path.pathtype = T_NestLoop;
pathnode->path.parent = joinrel;
pathnode->path.param_info =
get_joinrel_parampathinfo(root,
joinrel,
outer_path,
inner_path,
sjinfo,
required_outer,
&restrict_clauses);
pathnode->path.pathkeys = pathkeys;
pathnode->jointype = jointype;
pathnode->outerjoinpath = outer_path;
pathnode->innerjoinpath = inner_path;
pathnode->joinrestrictinfo = restrict_clauses;
final_cost_nestloop(root, pathnode, workspace, sjinfo, semifactors);
return pathnode;
}
| ResultPath* create_result_path | ( | List * | quals | ) |
Definition at line 1019 of file pathnode.c.
References cpu_tuple_cost, makeNode, Path::param_info, Path::parent, ResultPath::path, Path::pathkeys, Path::pathtype, ResultPath::quals, Path::rows, Path::startup_cost, and Path::total_cost.
Referenced by query_planner().
{
ResultPath *pathnode = makeNode(ResultPath);
pathnode->path.pathtype = T_Result;
pathnode->path.parent = NULL;
pathnode->path.param_info = NULL; /* there are no other rels... */
pathnode->path.pathkeys = NIL;
pathnode->quals = quals;
/* Hardly worth defining a cost_result() function ... just do it */
pathnode->path.rows = 1;
pathnode->path.startup_cost = 0;
pathnode->path.total_cost = cpu_tuple_cost;
/*
* In theory we should include the qual eval cost as well, but at present
* that doesn't accomplish much except duplicate work that will be done
* again in make_result; since this is only used for degenerate cases,
* nothing interesting will be done with the path cost values...
*/
return pathnode;
}
| Path* create_seqscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 693 of file pathnode.c.
References cost_seqscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by plan_cluster_use_sort(), reparameterize_path(), and set_plain_rel_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_SeqScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* seqscan has unordered result */
cost_seqscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| Path* create_subqueryscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | pathkeys, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1602 of file pathnode.c.
References cost_subqueryscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by reparameterize_path(), and set_subquery_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_SubqueryScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = pathkeys;
cost_subqueryscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| TidPath* create_tidscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| List * | tidquals, | |||
| Relids | required_outer | |||
| ) |
Definition at line 863 of file pathnode.c.
References cost_tidscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, TidPath::path, Path::pathkeys, Path::pathtype, and TidPath::tidquals.
Referenced by create_tidscan_paths().
{
TidPath *pathnode = makeNode(TidPath);
pathnode->path.pathtype = T_TidScan;
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->tidquals = tidquals;
cost_tidscan(&pathnode->path, root, rel, tidquals,
pathnode->path.param_info);
return pathnode;
}
| UniquePath* create_unique_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Path * | subpath, | |||
| SpecialJoinInfo * | sjinfo | |||
| ) |
Definition at line 1084 of file pathnode.c.
References AGG_HASHED, OpExpr::args, Assert, bms_equal(), bms_is_empty(), bms_is_subset(), bms_overlap(), bms_union(), RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, contain_volatile_functions(), copyObject(), cost_agg(), cost_sort(), cpu_operator_cost, enable_hashagg, estimate_num_groups(), exprType(), get_commutator(), get_mergejoin_opfamilies(), UniquePath::in_operators, IsA, SpecialJoinInfo::join_quals, JOIN_SEMI, SpecialJoinInfo::jointype, lappend(), lappend_oid(), lfirst, linitial, list_length(), lsecond, makeNode, MemoryContextSwitchTo(), NIL, NULL, OidIsValid, op_hashjoinable(), op_mergejoinable(), OpExpr::opno, Path::param_info, Path::parent, UniquePath::path, Path::pathkeys, Path::pathtype, PlannerInfo::planner_cxt, planner_rt_fetch, pull_varnos(), query_is_distinct_for(), relation_has_unique_index_for(), RelOptInfo::relid, RelOptInfo::relids, RelOptInfo::rows, Path::rows, RTE_RELATION, RTE_SUBQUERY, RelOptInfo::rtekind, Path::startup_cost, UniquePath::subpath, RangeTblEntry::subquery, SpecialJoinInfo::syn_righthand, Path::total_cost, translate_sub_tlist(), UniquePath::umethod, UniquePath::uniq_exprs, UNIQUE_PATH_HASH, RelOptInfo::width, and work_mem.
Referenced by hash_inner_and_outer(), join_is_legal(), make_join_rel(), match_unsorted_outer(), and sort_inner_and_outer().
{
UniquePath *pathnode;
Path sort_path; /* dummy for result of cost_sort */
Path agg_path; /* dummy for result of cost_agg */
MemoryContext oldcontext;
List *in_operators;
List *uniq_exprs;
bool all_btree;
bool all_hash;
int numCols;
ListCell *lc;
/* Caller made a mistake if subpath isn't cheapest_total ... */
Assert(subpath == rel->cheapest_total_path);
Assert(subpath->parent == rel);
/* ... or if SpecialJoinInfo is the wrong one */
Assert(sjinfo->jointype == JOIN_SEMI);
Assert(bms_equal(rel->relids, sjinfo->syn_righthand));
/* If result already cached, return it */
if (rel->cheapest_unique_path)
return (UniquePath *) rel->cheapest_unique_path;
/* If we previously failed, return NULL quickly */
if (sjinfo->join_quals == NIL)
return NULL;
/*
* We must ensure path struct and subsidiary data are allocated in main
* planning context; otherwise GEQO memory management causes trouble.
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
/*----------
* Look to see whether the semijoin's join quals consist of AND'ed
* equality operators, with (only) RHS variables on only one side of
* each one. If so, we can figure out how to enforce uniqueness for
* the RHS.
*
* Note that the input join_quals list is the list of quals that are
* *syntactically* associated with the semijoin, which in practice means
* the synthesized comparison list for an IN or the WHERE of an EXISTS.
* Particularly in the latter case, it might contain clauses that aren't
* *semantically* associated with the join, but refer to just one side or
* the other. We can ignore such clauses here, as they will just drop
* down to be processed within one side or the other. (It is okay to
* consider only the syntactically-associated clauses here because for a
* semijoin, no higher-level quals could refer to the RHS, and so there
* can be no other quals that are semantically associated with this join.
* We do things this way because it is useful to be able to run this test
* before we have extracted the list of quals that are actually
* semantically associated with the particular join.)
*
* Note that the in_operators list consists of the joinqual operators
* themselves (but commuted if needed to put the RHS value on the right).
* These could be cross-type operators, in which case the operator
* actually needed for uniqueness is a related single-type operator.
* We assume here that that operator will be available from the btree
* or hash opclass when the time comes ... if not, create_unique_plan()
* will fail.
*----------
*/
in_operators = NIL;
uniq_exprs = NIL;
all_btree = true;
all_hash = enable_hashagg; /* don't consider hash if not enabled */
foreach(lc, sjinfo->join_quals)
{
OpExpr *op = (OpExpr *) lfirst(lc);
Oid opno;
Node *left_expr;
Node *right_expr;
Relids left_varnos;
Relids right_varnos;
Relids all_varnos;
Oid opinputtype;
/* Is it a binary opclause? */
if (!IsA(op, OpExpr) ||
list_length(op->args) != 2)
{
/* No, but does it reference both sides? */
all_varnos = pull_varnos((Node *) op);
if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
bms_is_subset(all_varnos, sjinfo->syn_righthand))
{
/*
* Clause refers to only one rel, so ignore it --- unless it
* contains volatile functions, in which case we'd better
* punt.
*/
if (contain_volatile_functions((Node *) op))
goto no_unique_path;
continue;
}
/* Non-operator clause referencing both sides, must punt */
goto no_unique_path;
}
/* Extract data from binary opclause */
opno = op->opno;
left_expr = linitial(op->args);
right_expr = lsecond(op->args);
left_varnos = pull_varnos(left_expr);
right_varnos = pull_varnos(right_expr);
all_varnos = bms_union(left_varnos, right_varnos);
opinputtype = exprType(left_expr);
/* Does it reference both sides? */
if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
bms_is_subset(all_varnos, sjinfo->syn_righthand))
{
/*
* Clause refers to only one rel, so ignore it --- unless it
* contains volatile functions, in which case we'd better punt.
*/
if (contain_volatile_functions((Node *) op))
goto no_unique_path;
continue;
}
/* check rel membership of arguments */
if (!bms_is_empty(right_varnos) &&
bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
!bms_overlap(left_varnos, sjinfo->syn_righthand))
{
/* typical case, right_expr is RHS variable */
}
else if (!bms_is_empty(left_varnos) &&
bms_is_subset(left_varnos, sjinfo->syn_righthand) &&
!bms_overlap(right_varnos, sjinfo->syn_righthand))
{
/* flipped case, left_expr is RHS variable */
opno = get_commutator(opno);
if (!OidIsValid(opno))
goto no_unique_path;
right_expr = left_expr;
}
else
goto no_unique_path;
/* all operators must be btree equality or hash equality */
if (all_btree)
{
/* oprcanmerge is considered a hint... */
if (!op_mergejoinable(opno, opinputtype) ||
get_mergejoin_opfamilies(opno) == NIL)
all_btree = false;
}
if (all_hash)
{
/* ... but oprcanhash had better be correct */
if (!op_hashjoinable(opno, opinputtype))
all_hash = false;
}
if (!(all_btree || all_hash))
goto no_unique_path;
/* so far so good, keep building lists */
in_operators = lappend_oid(in_operators, opno);
uniq_exprs = lappend(uniq_exprs, copyObject(right_expr));
}
/* Punt if we didn't find at least one column to unique-ify */
if (uniq_exprs == NIL)
goto no_unique_path;
/*
* The expressions we'd need to unique-ify mustn't be volatile.
*/
if (contain_volatile_functions((Node *) uniq_exprs))
goto no_unique_path;
/*
* If we get here, we can unique-ify using at least one of sorting and
* hashing. Start building the result Path object.
*/
pathnode = makeNode(UniquePath);
pathnode->path.pathtype = T_Unique;
pathnode->path.parent = rel;
pathnode->path.param_info = subpath->param_info;
/*
* Assume the output is unsorted, since we don't necessarily have pathkeys
* to represent it. (This might get overridden below.)
*/
pathnode->path.pathkeys = NIL;
pathnode->subpath = subpath;
pathnode->in_operators = in_operators;
pathnode->uniq_exprs = uniq_exprs;
/*
* If the input is a relation and it has a unique index that proves the
* uniq_exprs are unique, then we don't need to do anything. Note that
* relation_has_unique_index_for automatically considers restriction
* clauses for the rel, as well.
*/
if (rel->rtekind == RTE_RELATION && all_btree &&
relation_has_unique_index_for(root, rel, NIL,
uniq_exprs, in_operators))
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
pathnode->path.pathkeys = subpath->pathkeys;
rel->cheapest_unique_path = (Path *) pathnode;
MemoryContextSwitchTo(oldcontext);
return pathnode;
}
/*
* If the input is a subquery whose output must be unique already, then we
* don't need to do anything. The test for uniqueness has to consider
* exactly which columns we are extracting; for example "SELECT DISTINCT
* x,y" doesn't guarantee that x alone is distinct. So we cannot check for
* this optimization unless uniq_exprs consists only of simple Vars
* referencing subquery outputs. (Possibly we could do something with
* expressions in the subquery outputs, too, but for now keep it simple.)
*/
if (rel->rtekind == RTE_SUBQUERY)
{
RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
List *sub_tlist_colnos;
sub_tlist_colnos = translate_sub_tlist(uniq_exprs, rel->relid);
if (sub_tlist_colnos &&
query_is_distinct_for(rte->subquery,
sub_tlist_colnos, in_operators))
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
pathnode->path.pathkeys = subpath->pathkeys;
rel->cheapest_unique_path = (Path *) pathnode;
MemoryContextSwitchTo(oldcontext);
return pathnode;
}
}
/* Estimate number of output rows */
pathnode->path.rows = estimate_num_groups(root, uniq_exprs, rel->rows);
numCols = list_length(uniq_exprs);
if (all_btree)
{
/*
* Estimate cost for sort+unique implementation
*/
cost_sort(&sort_path, root, NIL,
subpath->total_cost,
rel->rows,
rel->width,
0.0,
work_mem,
-1.0);
/*
* Charge one cpu_operator_cost per comparison per input tuple. We
* assume all columns get compared at most of the tuples. (XXX
* probably this is an overestimate.) This should agree with
* make_unique.
*/
sort_path.total_cost += cpu_operator_cost * rel->rows * numCols;
}
if (all_hash)
{
/*
* Estimate the overhead per hashtable entry at 64 bytes (same as in
* planner.c).
*/
int hashentrysize = rel->width + 64;
if (hashentrysize * pathnode->path.rows > work_mem * 1024L)
all_hash = false; /* don't try to hash */
else
cost_agg(&agg_path, root,
AGG_HASHED, NULL,
numCols, pathnode->path.rows,
subpath->startup_cost,
subpath->total_cost,
rel->rows);
}
if (all_btree && all_hash)
{
if (agg_path.total_cost < sort_path.total_cost)
pathnode->umethod = UNIQUE_PATH_HASH;
else
pathnode->umethod = UNIQUE_PATH_SORT;
}
else if (all_btree)
pathnode->umethod = UNIQUE_PATH_SORT;
else if (all_hash)
pathnode->umethod = UNIQUE_PATH_HASH;
else
goto no_unique_path;
if (pathnode->umethod == UNIQUE_PATH_HASH)
{
pathnode->path.startup_cost = agg_path.startup_cost;
pathnode->path.total_cost = agg_path.total_cost;
}
else
{
pathnode->path.startup_cost = sort_path.startup_cost;
pathnode->path.total_cost = sort_path.total_cost;
}
rel->cheapest_unique_path = (Path *) pathnode;
MemoryContextSwitchTo(oldcontext);
return pathnode;
no_unique_path: /* failure exit */
/* Mark the SpecialJoinInfo as not unique-able */
sjinfo->join_quals = NIL;
MemoryContextSwitchTo(oldcontext);
return NULL;
}
| Path* create_valuesscan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1646 of file pathnode.c.
References cost_valuesscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by set_values_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_ValuesScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* result is always unordered */
cost_valuesscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| Path* create_worktablescan_path | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 1689 of file pathnode.c.
References cost_ctescan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.
Referenced by set_worktable_pathlist().
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_WorkTableScan;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* result is always unordered */
/* Cost is the same as for a regular CTE scan */
cost_ctescan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
| RelOptInfo* find_base_rel | ( | PlannerInfo * | root, | |
| int | relid | |||
| ) |
Definition at line 196 of file relnode.c.
References Assert, elog, ERROR, and PlannerInfo::simple_rel_array.
Referenced by add_join_clause_to_rels(), add_placeholders_to_base_rels(), add_vars_to_targetlist(), build_joinrel_tlist(), clause_selectivity(), distribute_restrictinfo_to_rels(), examine_simple_variable(), examine_variable(), find_join_input_rel(), join_is_removable(), make_rel_from_joinlist(), remove_join_clause_from_rels(), remove_rel_from_query(), set_append_rel_size(), set_subquery_size_estimates(), and set_subqueryscan_references().
{
RelOptInfo *rel;
Assert(relid > 0);
if (relid < root->simple_rel_array_size)
{
rel = root->simple_rel_array[relid];
if (rel)
return rel;
}
elog(ERROR, "no relation entry for relid %d", relid);
return NULL; /* keep compiler quiet */
}
| AppendRelInfo* find_childrel_appendrelinfo | ( | PlannerInfo * | root, | |
| RelOptInfo * | rel | |||
| ) |
Definition at line 687 of file relnode.c.
References PlannerInfo::append_rel_list, Assert, AppendRelInfo::child_relid, elog, ERROR, lfirst, RelOptInfo::relid, RELOPT_OTHER_MEMBER_REL, and RelOptInfo::reloptkind.
Referenced by check_partial_indexes(), generate_implied_equalities_for_column(), and generate_join_implied_equalities().
{
Index relid = rel->relid;
ListCell *lc;
/* Should only be called on child rels */
Assert(rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
foreach(lc, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
if (appinfo->child_relid == relid)
return appinfo;
}
/* should have found the entry ... */
elog(ERROR, "child rel %d not found in append_rel_list", relid);
return NULL; /* not reached */
}
| RelOptInfo* find_join_rel | ( | PlannerInfo * | root, | |
| Relids | relids | |||
| ) |
Definition at line 261 of file relnode.c.
References bms_equal(), build_join_rel_hash(), HASH_FIND, hash_search(), JoinHashEntry::join_rel, PlannerInfo::join_rel_hash, PlannerInfo::join_rel_list, lfirst, list_length(), NULL, and RelOptInfo::relids.
Referenced by build_join_rel(), examine_variable(), and find_join_input_rel().
{
/*
* Switch to using hash lookup when list grows "too long". The threshold
* is arbitrary and is known only here.
*/
if (!root->join_rel_hash && list_length(root->join_rel_list) > 32)
build_join_rel_hash(root);
/*
* Use either hashtable lookup or linear search, as appropriate.
*
* Note: the seemingly redundant hashkey variable is used to avoid taking
* the address of relids; unless the compiler is exceedingly smart, doing
* so would force relids out of a register and thus probably slow down the
* list-search case.
*/
if (root->join_rel_hash)
{
Relids hashkey = relids;
JoinHashEntry *hentry;
hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
&hashkey,
HASH_FIND,
NULL);
if (hentry)
return hentry->join_rel;
}
else
{
ListCell *l;
foreach(l, root->join_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(l);
if (bms_equal(rel->relids, relids))
return rel;
}
}
return NULL;
}
| ParamPathInfo* get_appendrel_parampathinfo | ( | RelOptInfo * | appendrel, | |
| Relids | required_outer | |||
| ) |
Definition at line 942 of file relnode.c.
References Assert, bms_equal(), bms_is_empty(), bms_overlap(), lappend(), lfirst, makeNode, ParamPathInfo::ppi_clauses, ParamPathInfo::ppi_req_outer, ParamPathInfo::ppi_rows, RelOptInfo::ppilist, and RelOptInfo::relids.
Referenced by create_append_path(), and create_merge_append_path().
{
ParamPathInfo *ppi;
ListCell *lc;
/* Unparameterized paths have no ParamPathInfo */
if (bms_is_empty(required_outer))
return NULL;
Assert(!bms_overlap(appendrel->relids, required_outer));
/* If we already have a PPI for this parameterization, just return it */
foreach(lc, appendrel->ppilist)
{
ppi = (ParamPathInfo *) lfirst(lc);
if (bms_equal(ppi->ppi_req_outer, required_outer))
return ppi;
}
/* Else build the ParamPathInfo */
ppi = makeNode(ParamPathInfo);
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = 0;
ppi->ppi_clauses = NIL;
appendrel->ppilist = lappend(appendrel->ppilist, ppi);
return ppi;
}
| ParamPathInfo* get_baserel_parampathinfo | ( | PlannerInfo * | root, | |
| RelOptInfo * | baserel, | |||
| Relids | required_outer | |||
| ) |
Definition at line 720 of file relnode.c.
References Assert, bms_equal(), bms_is_empty(), bms_overlap(), bms_union(), generate_join_implied_equalities(), get_parameterized_baserel_size(), join_clause_is_movable_into(), RelOptInfo::joininfo, lappend(), lfirst, list_concat(), makeNode, ParamPathInfo::ppi_clauses, ParamPathInfo::ppi_req_outer, ParamPathInfo::ppi_rows, RelOptInfo::ppilist, and RelOptInfo::relids.
Referenced by bitmap_and_cost_est(), bitmap_scan_cost_est(), create_bitmap_heap_path(), create_ctescan_path(), create_foreignscan_path(), create_functionscan_path(), create_index_path(), create_seqscan_path(), create_subqueryscan_path(), create_tidscan_path(), create_valuesscan_path(), create_worktablescan_path(), and reparameterize_path().
{
ParamPathInfo *ppi;
Relids joinrelids;
List *pclauses;
double rows;
ListCell *lc;
/* Unparameterized paths have no ParamPathInfo */
if (bms_is_empty(required_outer))
return NULL;
Assert(!bms_overlap(baserel->relids, required_outer));
/* If we already have a PPI for this parameterization, just return it */
foreach(lc, baserel->ppilist)
{
ppi = (ParamPathInfo *) lfirst(lc);
if (bms_equal(ppi->ppi_req_outer, required_outer))
return ppi;
}
/*
* Identify all joinclauses that are movable to this base rel given this
* parameterization.
*/
joinrelids = bms_union(baserel->relids, required_outer);
pclauses = NIL;
foreach(lc, baserel->joininfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
if (join_clause_is_movable_into(rinfo,
baserel->relids,
joinrelids))
pclauses = lappend(pclauses, rinfo);
}
/*
* Add in joinclauses generated by EquivalenceClasses, too. (These
* necessarily satisfy join_clause_is_movable_into.)
*/
pclauses = list_concat(pclauses,
generate_join_implied_equalities(root,
joinrelids,
required_outer,
baserel));
/* Estimate the number of rows returned by the parameterized scan */
rows = get_parameterized_baserel_size(root, baserel, pclauses);
/* And now we can build the ParamPathInfo */
ppi = makeNode(ParamPathInfo);
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = rows;
ppi->ppi_clauses = pclauses;
baserel->ppilist = lappend(baserel->ppilist, ppi);
return ppi;
}
| ParamPathInfo* get_joinrel_parampathinfo | ( | PlannerInfo * | root, | |
| RelOptInfo * | joinrel, | |||
| Path * | outer_path, | |||
| Path * | inner_path, | |||
| SpecialJoinInfo * | sjinfo, | |||
| Relids | required_outer, | |||
| List ** | restrict_clauses | |||
| ) |
Definition at line 811 of file relnode.c.
References Assert, bms_equal(), bms_is_empty(), bms_overlap(), bms_union(), generate_join_implied_equalities(), get_parameterized_joinrel_size(), join_clause_is_movable_into(), RelOptInfo::joininfo, lappend(), lfirst, list_concat(), makeNode, Path::param_info, Path::parent, PATH_REQ_OUTER, ParamPathInfo::ppi_clauses, ParamPathInfo::ppi_req_outer, ParamPathInfo::ppi_rows, RelOptInfo::ppilist, RelOptInfo::relids, and Path::rows.
Referenced by create_hashjoin_path(), create_mergejoin_path(), and create_nestloop_path().
{
ParamPathInfo *ppi;
Relids join_and_req;
Relids outer_and_req;
Relids inner_and_req;
List *pclauses;
List *eclauses;
double rows;
ListCell *lc;
/* Unparameterized paths have no ParamPathInfo or extra join clauses */
if (bms_is_empty(required_outer))
return NULL;
Assert(!bms_overlap(joinrel->relids, required_outer));
/*
* Identify all joinclauses that are movable to this join rel given this
* parameterization. These are the clauses that are movable into this
* join, but not movable into either input path. Treat an unparameterized
* input path as not accepting parameterized clauses (because it won't,
* per the shortcut exit above), even though the joinclause movement rules
* might allow the same clauses to be moved into a parameterized path for
* that rel.
*/
join_and_req = bms_union(joinrel->relids, required_outer);
if (outer_path->param_info)
outer_and_req = bms_union(outer_path->parent->relids,
PATH_REQ_OUTER(outer_path));
else
outer_and_req = NULL; /* outer path does not accept parameters */
if (inner_path->param_info)
inner_and_req = bms_union(inner_path->parent->relids,
PATH_REQ_OUTER(inner_path));
else
inner_and_req = NULL; /* inner path does not accept parameters */
pclauses = NIL;
foreach(lc, joinrel->joininfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
if (join_clause_is_movable_into(rinfo,
joinrel->relids,
join_and_req) &&
!join_clause_is_movable_into(rinfo,
outer_path->parent->relids,
outer_and_req) &&
!join_clause_is_movable_into(rinfo,
inner_path->parent->relids,
inner_and_req))
pclauses = lappend(pclauses, rinfo);
}
/* Consider joinclauses generated by EquivalenceClasses, too */
eclauses = generate_join_implied_equalities(root,
join_and_req,
required_outer,
joinrel);
/* We only want ones that aren't movable to lower levels */
foreach(lc, eclauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(join_clause_is_movable_into(rinfo,
joinrel->relids,
join_and_req));
if (!join_clause_is_movable_into(rinfo,
outer_path->parent->relids,
outer_and_req) &&
!join_clause_is_movable_into(rinfo,
inner_path->parent->relids,
inner_and_req))
pclauses = lappend(pclauses, rinfo);
}
/*
* Now, attach the identified moved-down clauses to the caller's
* restrict_clauses list. By using list_concat in this order, we leave
* the original list structure of restrict_clauses undamaged.
*/
*restrict_clauses = list_concat(pclauses, *restrict_clauses);
/* If we already have a PPI for this parameterization, just return it */
foreach(lc, joinrel->ppilist)
{
ppi = (ParamPathInfo *) lfirst(lc);
if (bms_equal(ppi->ppi_req_outer, required_outer))
return ppi;
}
/* Estimate the number of rows returned by the parameterized join */
rows = get_parameterized_joinrel_size(root, joinrel,
outer_path->rows,
inner_path->rows,
sjinfo,
*restrict_clauses);
/*
* And now we can build the ParamPathInfo. No point in saving the
* input-pair-dependent clause list, though.
*
* Note: in GEQO mode, we'll be called in a temporary memory context, but
* the joinrel structure is there too, so no problem.
*/
ppi = makeNode(ParamPathInfo);
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = rows;
ppi->ppi_clauses = NIL;
joinrel->ppilist = lappend(joinrel->ppilist, ppi);
return ppi;
}
| Path* reparameterize_path | ( | PlannerInfo * | root, | |
| Path * | path, | |||
| Relids | required_outer, | |||
| double | loop_count | |||
| ) |
Definition at line 2019 of file pathnode.c.
References BitmapHeapPath::bitmapqual, bms_is_subset(), cost_index(), create_bitmap_heap_path(), create_seqscan_path(), create_subqueryscan_path(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, IndexPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, T_BitmapHeapScan, T_IndexOnlyScan, T_IndexScan, T_SeqScan, and T_SubqueryScan.
Referenced by set_append_rel_pathlist().
{
RelOptInfo *rel = path->parent;
/* Can only increase, not decrease, path's parameterization */
if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer))
return NULL;
switch (path->pathtype)
{
case T_SeqScan:
return create_seqscan_path(root, rel, required_outer);
case T_IndexScan:
case T_IndexOnlyScan:
{
IndexPath *ipath = (IndexPath *) path;
IndexPath *newpath = makeNode(IndexPath);
/*
* We can't use create_index_path directly, and would not want
* to because it would re-compute the indexqual conditions
* which is wasted effort. Instead we hack things a bit:
* flat-copy the path node, revise its param_info, and redo
* the cost estimate.
*/
memcpy(newpath, ipath, sizeof(IndexPath));
newpath->path.param_info =
get_baserel_parampathinfo(root, rel, required_outer);
cost_index(newpath, root, loop_count);
return (Path *) newpath;
}
case T_BitmapHeapScan:
{
BitmapHeapPath *bpath = (BitmapHeapPath *) path;
return (Path *) create_bitmap_heap_path(root,
rel,
bpath->bitmapqual,
required_outer,
loop_count);
}
case T_SubqueryScan:
return create_subqueryscan_path(root, rel, path->pathkeys,
required_outer);
default:
break;
}
return NULL;
}
| void set_cheapest | ( | RelOptInfo * | parent_rel | ) |
Definition at line 226 of file pathnode.c.
References Assert, BMS_DIFFERENT, BMS_EQUAL, BMS_SUBSET1, BMS_SUBSET2, bms_subset_compare(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_startup_path, RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, compare_path_costs(), compare_pathkeys(), elog, ERROR, IsA, lappend(), lcons(), lfirst, NIL, NULL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER2, RelOptInfo::pathlist, STARTUP_COST, and TOTAL_COST.
Referenced by mark_dummy_rel(), merge_clump(), set_append_rel_pathlist(), set_cte_pathlist(), set_dummy_rel_pathlist(), set_foreign_pathlist(), set_function_pathlist(), set_plain_rel_pathlist(), set_subquery_pathlist(), set_values_pathlist(), set_worktable_pathlist(), and standard_join_search().
{
Path *cheapest_startup_path;
Path *cheapest_total_path;
Path *best_param_path;
List *parameterized_paths;
ListCell *p;
Assert(IsA(parent_rel, RelOptInfo));
if (parent_rel->pathlist == NIL)
elog(ERROR, "could not devise a query plan for the given query");
cheapest_startup_path = cheapest_total_path = best_param_path = NULL;
parameterized_paths = NIL;
foreach(p, parent_rel->pathlist)
{
Path *path = (Path *) lfirst(p);
int cmp;
if (path->param_info)
{
/* Parameterized path, so add it to parameterized_paths */
parameterized_paths = lappend(parameterized_paths, path);
/*
* If we have an unparameterized cheapest-total, we no longer care
* about finding the best parameterized path, so move on.
*/
if (cheapest_total_path)
continue;
/*
* Otherwise, track the best parameterized path, which is the one
* with least total cost among those of the minimum
* parameterization.
*/
if (best_param_path == NULL)
best_param_path = path;
else
{
switch (bms_subset_compare(PATH_REQ_OUTER(path),
PATH_REQ_OUTER(best_param_path)))
{
case BMS_EQUAL:
/* keep the cheaper one */
if (compare_path_costs(path, best_param_path,
TOTAL_COST) < 0)
best_param_path = path;
break;
case BMS_SUBSET1:
/* new path is less-parameterized */
best_param_path = path;
break;
case BMS_SUBSET2:
/* old path is less-parameterized, keep it */
break;
case BMS_DIFFERENT:
/*
* This means that neither path has the least possible
* parameterization for the rel. We'll sit on the old
* path until something better comes along.
*/
break;
}
}
}
else
{
/* Unparameterized path, so consider it for cheapest slots */
if (cheapest_total_path == NULL)
{
cheapest_startup_path = cheapest_total_path = path;
continue;
}
/*
* If we find two paths of identical costs, try to keep the
* better-sorted one. The paths might have unrelated sort
* orderings, in which case we can only guess which might be
* better to keep, but if one is superior then we definitely
* should keep that one.
*/
cmp = compare_path_costs(cheapest_startup_path, path, STARTUP_COST);
if (cmp > 0 ||
(cmp == 0 &&
compare_pathkeys(cheapest_startup_path->pathkeys,
path->pathkeys) == PATHKEYS_BETTER2))
cheapest_startup_path = path;
cmp = compare_path_costs(cheapest_total_path, path, TOTAL_COST);
if (cmp > 0 ||
(cmp == 0 &&
compare_pathkeys(cheapest_total_path->pathkeys,
path->pathkeys) == PATHKEYS_BETTER2))
cheapest_total_path = path;
}
}
/* Add cheapest unparameterized path, if any, to parameterized_paths */
if (cheapest_total_path)
parameterized_paths = lcons(cheapest_total_path, parameterized_paths);
/*
* If there is no unparameterized path, use the best parameterized path
* as cheapest_total_path (but not as cheapest_startup_path).
*/
if (cheapest_total_path == NULL)
cheapest_total_path = best_param_path;
Assert(cheapest_total_path != NULL);
parent_rel->cheapest_startup_path = cheapest_startup_path;
parent_rel->cheapest_total_path = cheapest_total_path;
parent_rel->cheapest_unique_path = NULL; /* computed only if needed */
parent_rel->cheapest_parameterized_paths = parameterized_paths;
}
| void setup_simple_rel_arrays | ( | PlannerInfo * | root | ) |
Definition at line 54 of file relnode.c.
References lfirst, list_length(), palloc0(), PlannerInfo::parse, Query::rtable, PlannerInfo::simple_rel_array, PlannerInfo::simple_rel_array_size, and PlannerInfo::simple_rte_array.
Referenced by plan_cluster_use_sort(), plan_set_operations(), and query_planner().
{
Index rti;
ListCell *lc;
/* Arrays are accessed using RT indexes (1..N) */
root->simple_rel_array_size = list_length(root->parse->rtable) + 1;
/* simple_rel_array is initialized to all NULLs */
root->simple_rel_array = (RelOptInfo **)
palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *));
/* simple_rte_array is an array equivalent of the rtable list */
root->simple_rte_array = (RangeTblEntry **)
palloc0(root->simple_rel_array_size * sizeof(RangeTblEntry *));
rti = 1;
foreach(lc, root->parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
root->simple_rte_array[rti++] = rte;
}
}
1.7.1