#include "postgres.h"
#include <math.h>
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
Go to the source code of this file.
enum PathCostComparison |
Definition at line 32 of file pathnode.c.
{ COSTS_EQUAL, /* path costs are fuzzily equal */ COSTS_BETTER1, /* first path is cheaper than second */ COSTS_BETTER2, /* second path is cheaper than first */ COSTS_DIFFERENT /* neither path dominates the other on cost */ } PathCostComparison;
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; }
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; }
static PathCostComparison compare_path_costs_fuzzily | ( | Path * | path1, | |
Path * | path2, | |||
double | fuzz_factor, | |||
bool | consider_startup | |||
) | [static] |
Definition at line 152 of file pathnode.c.
References NULL, Path::param_info, Path::startup_cost, and Path::total_cost.
Referenced by add_path().
{ /* * Check total cost first since it's more likely to be different; many * paths have zero startup cost. */ if (path1->total_cost > path2->total_cost * fuzz_factor) { /* path1 fuzzily worse on total cost */ if (consider_startup && path2->startup_cost > path1->startup_cost * fuzz_factor && path1->param_info == NULL) { /* ... but path2 fuzzily worse on startup, so DIFFERENT */ return COSTS_DIFFERENT; } /* else path2 dominates */ return COSTS_BETTER2; } if (path2->total_cost > path1->total_cost * fuzz_factor) { /* path2 fuzzily worse on total cost */ if (consider_startup && path1->startup_cost > path2->startup_cost * fuzz_factor && path2->param_info == NULL) { /* ... but path1 fuzzily worse on startup, so DIFFERENT */ return COSTS_DIFFERENT; } /* else path1 dominates */ return COSTS_BETTER1; } /* fuzzily the same on total cost */ /* (so we may as well compare startup cost, even if !consider_startup) */ if (path1->startup_cost > path2->startup_cost * fuzz_factor && path2->param_info == NULL) { /* ... but path1 fuzzily worse on startup, so path2 wins */ return COSTS_BETTER2; } if (path2->startup_cost > path1->startup_cost * fuzz_factor && path1->param_info == NULL) { /* ... but path2 fuzzily worse on startup, so path1 wins */ return COSTS_BETTER1; } /* fuzzily the same on both costs */ return COSTS_EQUAL; }
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; }
Definition at line 1583 of file pathnode.c.
References forboth, lfirst_int, and lfirst_oid.
Referenced by query_is_distinct_for().
{ ListCell *lc1, *lc2; forboth(lc1, colnos, lc2, opids) { if (colno == lfirst_int(lc1)) return lfirst_oid(lc2); } return InvalidOid; }
Definition at line 1468 of file pathnode.c.
References SetOperationStmt::all, Assert, distinct_col_search(), Query::distinctClause, SortGroupClause::eqop, equality_ops_are_compatible(), get_sortgroupclause_tle(), Query::groupClause, SetOperationStmt::groupClauses, Query::hasAggs, Query::havingQual, IsA, lfirst, list_head(), list_length(), lnext, NULL, OidIsValid, SetOperationStmt::op, TargetEntry::resjunk, TargetEntry::resno, SETOP_NONE, Query::setOperations, and Query::targetList.
Referenced by create_unique_path().
{ ListCell *l; Oid opid; Assert(list_length(colnos) == list_length(opids)); /* * DISTINCT (including DISTINCT ON) guarantees uniqueness if all the * columns in the DISTINCT clause appear in colnos and operator semantics * match. */ if (query->distinctClause) { foreach(l, query->distinctClause) { SortGroupClause *sgc = (SortGroupClause *) lfirst(l); TargetEntry *tle = get_sortgroupclause_tle(sgc, query->targetList); opid = distinct_col_search(tle->resno, colnos, opids); if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop)) break; /* exit early if no match */ } if (l == NULL) /* had matches for all? */ return true; } /* * Similarly, GROUP BY guarantees uniqueness if all the grouped columns * appear in colnos and operator semantics match. */ if (query->groupClause) { foreach(l, query->groupClause) { SortGroupClause *sgc = (SortGroupClause *) lfirst(l); TargetEntry *tle = get_sortgroupclause_tle(sgc, query->targetList); opid = distinct_col_search(tle->resno, colnos, opids); if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop)) break; /* exit early if no match */ } if (l == NULL) /* had matches for all? */ return true; } else { /* * If we have no GROUP BY, but do have aggregates or HAVING, then the * result is at most one row so it's surely unique, for any operators. */ if (query->hasAggs || query->havingQual) return true; } /* * UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row, * except with ALL. */ if (query->setOperations) { SetOperationStmt *topop = (SetOperationStmt *) query->setOperations; Assert(IsA(topop, SetOperationStmt)); Assert(topop->op != SETOP_NONE); if (!topop->all) { ListCell *lg; /* We're good if all the nonjunk output columns are in colnos */ lg = list_head(topop->groupClauses); foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); SortGroupClause *sgc; if (tle->resjunk) continue; /* ignore resjunk columns */ /* non-resjunk columns should have grouping clauses */ Assert(lg != NULL); sgc = (SortGroupClause *) lfirst(lg); lg = lnext(lg); opid = distinct_col_search(tle->resno, colnos, opids); if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop)) break; /* exit early if no match */ } if (l == NULL) /* had matches for all? */ return true; } } /* * XXX Are there any other cases in which we can easily see the result * must be distinct? */ return false; }
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; }
Definition at line 1434 of file pathnode.c.
References IsA, lappend_int(), lfirst, NIL, Var::varattno, and Var::varno.
Referenced by create_unique_path().