⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pathnode.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * 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 + -