📄 pathnode.c
字号:
/*------------------------------------------------------------------------- * * pathnode.c * Routines to manipulate pathlists and create path nodes * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.125 2005/10/15 02:49:21 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <math.h>#include "catalog/pg_operator.h"#include "executor/executor.h"#include "miscadmin.h"#include "nodes/plannodes.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/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parsetree.h"#include "utils/memutils.h"#include "utils/selfuncs.h"#include "utils/syscache.h"static List *translate_sub_tlist(List *tlist, int relid);static bool query_is_distinct_for(Query *query, List *colnos);static bool hash_safe_tlist(List *tlist);/***************************************************************************** * MISC. PATH UTILITIES *****************************************************************************//* * compare_path_costs * Return -1, 0, or +1 according as path1 is cheaper, the same cost, * or more expensive than path2 for the specified criterion. */intcompare_path_costs(Path *path1, Path *path2, CostSelector criterion){ 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;}/* * compare_fuzzy_path_costs * Return -1, 0, or +1 according as path1 is cheaper, the same cost, * or more expensive than path2 for the specified criterion. * * This differs from compare_path_costs in that we consider the costs the * same if they agree to within a "fuzz factor". This is used by add_path * to avoid keeping both of a pair of paths that really have insignificantly * different cost. */static intcompare_fuzzy_path_costs(Path *path1, Path *path2, CostSelector criterion){ /* * We use a fuzz factor of 1% of the smaller cost. * * XXX does this percentage need to be user-configurable? */ if (criterion == STARTUP_COST) { if (path1->startup_cost > path2->startup_cost * 1.01) return +1; if (path2->startup_cost > path1->startup_cost * 1.01) 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 * 1.01) return +1; if (path2->total_cost > path1->total_cost * 1.01) return -1; } else { if (path1->total_cost > path2->total_cost * 1.01) return +1; if (path2->total_cost > path1->total_cost * 1.01) return -1; /* * If paths have the same total cost, order them by startup cost. */ if (path1->startup_cost > path2->startup_cost * 1.01) return +1; if (path2->startup_cost > path1->startup_cost * 1.01) return -1; } return 0;}/* * compare_path_fractional_costs * Return -1, 0, or +1 according as path1 is cheaper, the same cost, * or more expensive than path2 for fetching the specified fraction * of the total tuples. * * If fraction is <= 0 or > 1, we interpret it as 1, ie, we select the * path with the cheaper total_cost. */intcompare_fractional_path_costs(Path *path1, Path *path2, double fraction){ 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;}/* * set_cheapest * Find the minimum-cost paths from among a relation's paths, * and save them in the rel's cheapest-path fields. * * This is normally called only after we've finished constructing the path * list for the rel node. * * 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 it. */voidset_cheapest(RelOptInfo *parent_rel){ List *pathlist = parent_rel->pathlist; ListCell *p; Path *cheapest_startup_path; Path *cheapest_total_path; Assert(IsA(parent_rel, RelOptInfo)); if (pathlist == NIL) elog(ERROR, "could not devise a query plan for the given query"); cheapest_startup_path = cheapest_total_path = (Path *) linitial(pathlist); for_each_cell(p, lnext(list_head(pathlist))) { Path *path = (Path *) lfirst(p); int cmp; 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; } 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 */}/* * add_path * Consider a potential implementation path for the specified parent rel, * and add it to the rel's pathlist if it is worthy of consideration. * A path is worthy if it has either a better sort order (better pathkeys) * or cheaper cost (on either dimension) than any of the existing old paths. * * We also remove from the rel's pathlist any old paths that are dominated * by new_path --- that is, new_path is both cheaper and at least as well * ordered. * * The pathlist is kept sorted by TOTAL_COST metric, with cheaper paths * at the front. No code depends on that for correctness; it's simply * a speed hack within this routine. Doing it that way makes it more * likely that we will reject an inferior path after a few comparisons, * rather than many comparisons. * * NOTE: discarded Path objects are immediately pfree'd to reduce planner * memory consumption. We dare not try to free the substructure of a Path, * since much of it may be shared with other Paths or the query tree itself; * but just recycling discarded Path nodes is a very useful savings in * a large join tree. We can recycle the List nodes of pathlist, too. * * BUT: we do not pfree IndexPath objects, since they may be referenced as * children of BitmapHeapPaths as well as being paths in their own right. * * 'parent_rel' is the relation entry to which the path corresponds. * 'new_path' is a potential path for parent_rel. * * Returns nothing, but modifies parent_rel->pathlist. */voidadd_path(RelOptInfo *parent_rel, Path *new_path){ bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ ListCell *p1_prev = NULL; ListCell *p1; /* * 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(); /* * 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. */ p1 = list_head(parent_rel->pathlist); /* cannot use foreach here */ while (p1 != NULL) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ int costcmp; /* * As of Postgres 8.0, we use fuzzy cost comparison to avoid wasting * cycles keeping paths that are really not significantly different in * cost. */ costcmp = compare_fuzzy_path_costs(new_path, old_path, TOTAL_COST); /* * If the two paths compare differently for startup and total cost, * then we want to keep both, and we can skip the (much slower) * comparison of pathkeys. If they compare the same, proceed with the * pathkeys comparison. Note: this test relies on the fact that * compare_fuzzy_path_costs will only return 0 if both costs are * effectively equal (and, therefore, there's no need to call it twice * in that case). */ if (costcmp == 0 || costcmp == compare_fuzzy_path_costs(new_path, old_path, STARTUP_COST)) { switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys)) { case PATHKEYS_EQUAL: if (costcmp < 0) remove_old = true; /* new dominates old */ else if (costcmp > 0) accept_new = false; /* old dominates new */ else { /* * Same pathkeys, and fuzzily the same cost, so keep * just one --- but we'll do an exact cost comparison * to decide which. */ if (compare_path_costs(new_path, old_path, TOTAL_COST) < 0) remove_old = true; /* new dominates old */ else accept_new = false; /* old equals or dominates new */ } break; case PATHKEYS_BETTER1: if (costcmp <= 0) remove_old = true; /* new dominates old */ break; case PATHKEYS_BETTER2: if (costcmp >= 0) accept_new = false; /* old dominates new */ break; case PATHKEYS_DIFFERENT: /* keep both paths, since they have different ordering */ 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); /* Advance list pointer */ if (p1_prev) p1 = lnext(p1_prev); else p1 = list_head(parent_rel->pathlist); } else { /* new belongs after this old path if it has cost >= old's */ if (costcmp >= 0) insert_after = p1; /* Advance list pointers */ p1_prev = p1; p1 = lnext(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); }}/***************************************************************************** * PATH NODE CREATION ROUTINES *****************************************************************************//* * create_seqscan_path * Creates a path corresponding to a sequential scan, returning the * pathnode. */Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel){ Path *pathnode = makeNode(Path); pathnode->pathtype = T_SeqScan; pathnode->parent = rel; pathnode->pathkeys = NIL; /* seqscan has unordered result */ cost_seqscan(pathnode, root, rel); return pathnode;}/* * create_index_path * Creates a path node for an index scan. * * 'index' is a usable index.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -