pathnode.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 863 行 · 第 1/2 页

C
863
字号
/*------------------------------------------------------------------------- * * pathnode.c *	  Routines to manipulate pathlists and create path nodes * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.95 2003/08/04 02:40:01 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/cost.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"#include "optimizer/restrictinfo.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "utils/memutils.h"#include "utils/selfuncs.h"#include "utils/syscache.h"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_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;	List	   *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 *) lfirst(pathlist);	foreach(p, lnext(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. * *	  Unless parent_rel->pruneable is false, 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. * * '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 */	List	   *insert_after = NIL;		/* where to insert new item */	List	   *p1_prev = NIL;	List	   *p1;	/*	 * 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 = parent_rel->pathlist;	/* cannot use foreach here */	while (p1 != NIL)	{		Path	   *old_path = (Path *) lfirst(p1);		bool		remove_old = false; /* unless new proves superior */		int			costcmp;		costcmp = compare_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_path_costs will only return 0 if both		 * costs are equal (and, therefore, there's no need to call it		 * twice in that case).		 */		if (costcmp == 0 ||			costcmp == compare_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						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,		 * unless xfunc told us not to remove any paths.		 */		if (remove_old && parent_rel->pruneable)		{			List	   *p1_next = lnext(p1);			if (p1_prev)				lnext(p1_prev) = p1_next;			else				parent_rel->pathlist = p1_next;			pfree(old_path);			pfree(p1);			/* this is why we can't use foreach */			p1 = p1_next;		}		else		{			/* new belongs after this old path if it has cost >= old's */			if (costcmp >= 0)				insert_after = p1;			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)			lnext(insert_after) = lcons(new_path, lnext(insert_after));		else			parent_rel->pathlist = lcons(new_path, parent_rel->pathlist);	}	else	{		/* Reject and recycle the new path */		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(Query *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. * * 'rel' is the parent rel * 'index' is an index on 'rel' * 'restriction_clauses' is a list of lists of RestrictInfo nodes *			to be used as index qual conditions in the scan. * 'pathkeys' describes the ordering of the path. * 'indexscandir' is ForwardScanDirection or BackwardScanDirection *			for an ordered index, or NoMovementScanDirection for *			an unordered index. * * Returns the new path node. */IndexPath *create_index_path(Query *root,				  RelOptInfo *rel,				  IndexOptInfo *index,				  List *restriction_clauses,				  List *pathkeys,				  ScanDirection indexscandir){	IndexPath  *pathnode = makeNode(IndexPath);	List	   *indexquals;	pathnode->path.pathtype = T_IndexScan;	pathnode->path.parent = rel;	pathnode->path.pathkeys = pathkeys;	/* Convert RestrictInfo nodes to indexquals the executor can handle */	indexquals = expand_indexqual_conditions(index, restriction_clauses);	/*	 * We are making a pathnode for a single-scan indexscan; therefore,	 * both indexinfo and indexqual should be single-element lists.	 */	pathnode->indexinfo = makeList1(index);	pathnode->indexqual = makeList1(indexquals);	/* It's not an innerjoin path. */	pathnode->indexjoinclauses = NIL;	pathnode->indexscandir = indexscandir;	/*	 * The number of rows is the same as the parent rel's estimate, since	 * this isn't a join inner indexscan.	 */	pathnode->rows = rel->rows;	/*	 * Not sure if this is necessary, but it should help if the statistics	 * are too far off	 */	if (index->indpred && index->tuples < pathnode->rows)		pathnode->rows = index->tuples;	cost_index(&pathnode->path, root, rel, index, indexquals, false);	return pathnode;}/* * create_tidscan_path *	  Creates a path corresponding to a tid_direct scan, returning the *	  pathnode. */TidPath *create_tidscan_path(Query *root, RelOptInfo *rel, List *tideval){	TidPath    *pathnode = makeNode(TidPath);	pathnode->path.pathtype = T_TidScan;	pathnode->path.parent = rel;	pathnode->path.pathkeys = NIL;	pathnode->tideval = tideval;	cost_tidscan(&pathnode->path, root, rel, tideval);	/*	 * divide selectivity for each clause to get an equal selectivity as	 * IndexScan does OK ?	 */	return pathnode;}/* * create_append_path *	  Creates a path corresponding to an Append plan, returning the *	  pathnode. */AppendPath *create_append_path(RelOptInfo *rel, List *subpaths){	AppendPath *pathnode = makeNode(AppendPath);	List	   *l;	pathnode->path.pathtype = T_Append;	pathnode->path.parent = rel;	pathnode->path.pathkeys = NIL;		/* result is always considered										 * unsorted */	pathnode->subpaths = subpaths;	pathnode->path.startup_cost = 0;	pathnode->path.total_cost = 0;	foreach(l, subpaths)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?