pathkeys.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,494 行 · 第 1/3 页

C
1,494
字号
/*------------------------------------------------------------------------- * * pathkeys.c *	  Utilities for matching and building path keys * * See src/backend/optimizer/README for a great deal of information about * the nature and use of path keys. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.93 2008/01/09 20:42:28 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/skey.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "nodes/plannodes.h"#include "optimizer/clauses.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"#include "optimizer/tlist.h"#include "parser/parsetree.h"#include "parser/parse_expr.h"#include "utils/lsyscache.h"static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,			int strategy, bool nulls_first);static PathKey *make_canonical_pathkey(PlannerInfo *root,					   EquivalenceClass *eclass, Oid opfamily,					   int strategy, bool nulls_first);static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,						   Expr *expr, Oid ordering_op,						   bool nulls_first,						   Index sortref,						   bool canonicalize);static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,				  AttrNumber varattno);static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);/**************************************************************************** *		PATHKEY CONSTRUCTION AND REDUNDANCY TESTING ****************************************************************************//* * makePathKey *		create a PathKey node * * This does not promise to create a canonical PathKey, it's merely a * convenience routine to build the specified node. */static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,			int strategy, bool nulls_first){	PathKey    *pk = makeNode(PathKey);	pk->pk_eclass = eclass;	pk->pk_opfamily = opfamily;	pk->pk_strategy = strategy;	pk->pk_nulls_first = nulls_first;	return pk;}/* * make_canonical_pathkey *	  Given the parameters for a PathKey, find any pre-existing matching *	  pathkey in the query's list of "canonical" pathkeys.  Make a new *	  entry if there's not one already. * * Note that this function must not be used until after we have completed * merging EquivalenceClasses. */static PathKey *make_canonical_pathkey(PlannerInfo *root,					   EquivalenceClass *eclass, Oid opfamily,					   int strategy, bool nulls_first){	PathKey    *pk;	ListCell   *lc;	MemoryContext oldcontext;	/* The passed eclass might be non-canonical, so chase up to the top */	while (eclass->ec_merged)		eclass = eclass->ec_merged;	foreach(lc, root->canon_pathkeys)	{		pk = (PathKey *) lfirst(lc);		if (eclass == pk->pk_eclass &&			opfamily == pk->pk_opfamily &&			strategy == pk->pk_strategy &&			nulls_first == pk->pk_nulls_first)			return pk;	}	/*	 * Be sure canonical pathkeys are allocated in the main planning context.	 * Not an issue in normal planning, but it is for GEQO.	 */	oldcontext = MemoryContextSwitchTo(root->planner_cxt);	pk = makePathKey(eclass, opfamily, strategy, nulls_first);	root->canon_pathkeys = lappend(root->canon_pathkeys, pk);	MemoryContextSwitchTo(oldcontext);	return pk;}/* * pathkey_is_redundant *	   Is a pathkey redundant with one already in the given list? * * Both the given pathkey and the list members must be canonical for this * to work properly.  We detect two cases: * * 1. If the new pathkey's equivalence class contains a constant, and isn't * below an outer join, then we can disregard it as a sort key.  An example: *			SELECT ... WHERE x = 42 ORDER BY x, y; * We may as well just sort by y.  Note that because of opfamily matching, * this is semantically correct: we know that the equality constraint is one * that actually binds the variable to a single value in the terms of any * ordering operator that might go with the eclass.  This rule not only lets * us simplify (or even skip) explicit sorts, but also allows matching index * sort orders to a query when there are don't-care index columns. * * 2. If the new pathkey's equivalence class is the same as that of any * existing member of the pathkey list, then it is redundant.  Some examples: *			SELECT ... ORDER BY x, x; *			SELECT ... ORDER BY x, x DESC; *			SELECT ... WHERE x = y ORDER BY x, y; * In all these cases the second sort key cannot distinguish values that are * considered equal by the first, and so there's no point in using it. * Note in particular that we need not compare opfamily (all the opfamilies * of the EC have the same notion of equality) nor sort direction. * * Because the equivclass.c machinery forms only one copy of any EC per query, * pointer comparison is enough to decide whether canonical ECs are the same. */static boolpathkey_is_redundant(PathKey *new_pathkey, List *pathkeys){	EquivalenceClass *new_ec = new_pathkey->pk_eclass;	ListCell   *lc;	/* Assert we've been given canonical pathkeys */	Assert(!new_ec->ec_merged);	/* Check for EC containing a constant --- unconditionally redundant */	if (EC_MUST_BE_REDUNDANT(new_ec))		return true;	/* If same EC already used in list, then redundant */	foreach(lc, pathkeys)	{		PathKey    *old_pathkey = (PathKey *) lfirst(lc);		/* Assert we've been given canonical pathkeys */		Assert(!old_pathkey->pk_eclass->ec_merged);		if (new_ec == old_pathkey->pk_eclass)			return true;	}	return false;}/* * canonicalize_pathkeys *	   Convert a not-necessarily-canonical pathkeys list to canonical form. * * Note that this function must not be used until after we have completed * merging EquivalenceClasses. */List *canonicalize_pathkeys(PlannerInfo *root, List *pathkeys){	List	   *new_pathkeys = NIL;	ListCell   *l;	foreach(l, pathkeys)	{		PathKey    *pathkey = (PathKey *) lfirst(l);		EquivalenceClass *eclass;		PathKey    *cpathkey;		/* Find the canonical (merged) EquivalenceClass */		eclass = pathkey->pk_eclass;		while (eclass->ec_merged)			eclass = eclass->ec_merged;		/*		 * If we can tell it's redundant just from the EC, skip.		 * pathkey_is_redundant would notice that, but we needn't even bother		 * constructing the node...		 */		if (EC_MUST_BE_REDUNDANT(eclass))			continue;		/* OK, build a canonicalized PathKey struct */		cpathkey = make_canonical_pathkey(root,										  eclass,										  pathkey->pk_opfamily,										  pathkey->pk_strategy,										  pathkey->pk_nulls_first);		/* Add to list unless redundant */		if (!pathkey_is_redundant(cpathkey, new_pathkeys))			new_pathkeys = lappend(new_pathkeys, cpathkey);	}	return new_pathkeys;}/* * make_pathkey_from_sortinfo *	  Given an expression, a sortop, and a nulls-first flag, create *	  a PathKey.  If canonicalize = true, the result is a "canonical" *	  PathKey, otherwise not.  (But note it might be redundant anyway.) * * If the PathKey is being generated from a SortClause, sortref should be * the SortClause's SortGroupRef; otherwise zero. * * canonicalize should always be TRUE after EquivalenceClass merging has * been performed, but FALSE if we haven't done EquivalenceClass merging yet. */static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,						   Expr *expr, Oid ordering_op,						   bool nulls_first,						   Index sortref,						   bool canonicalize){	Oid			opfamily,				opcintype;	int16		strategy;	Oid			equality_op;	List	   *opfamilies;	EquivalenceClass *eclass;	/*	 * An ordering operator fully determines the behavior of its opfamily, so	 * could only meaningfully appear in one family --- or perhaps two if one	 * builds a reverse-sort opfamily, but there's not much point in that	 * anymore.  But EquivalenceClasses need to contain opfamily lists based	 * on the family membership of equality operators, which could easily be	 * bigger.	So, look up the equality operator that goes with the ordering	 * operator (this should be unique) and get its membership.	 */	/* Find the operator in pg_amop --- failure shouldn't happen */	if (!get_ordering_op_properties(ordering_op,									&opfamily, &opcintype, &strategy))		elog(ERROR, "operator %u is not a valid ordering operator",			 ordering_op);	/* Get matching equality operator */	equality_op = get_opfamily_member(opfamily,									  opcintype,									  opcintype,									  BTEqualStrategyNumber);	if (!OidIsValid(equality_op))		/* shouldn't happen */		elog(ERROR, "could not find equality operator for ordering operator %u",			 ordering_op);	opfamilies = get_mergejoin_opfamilies(equality_op);	if (!opfamilies)			/* certainly should find some */		elog(ERROR, "could not find opfamilies for ordering operator %u",			 ordering_op);	/*	 * When dealing with binary-compatible opclasses, we have to ensure that	 * the exposed type of the expression tree matches the declared input type	 * of the opclass, except when that is a polymorphic type (compare the	 * behavior of parse_coerce.c).  This ensures that we can correctly match	 * the indexkey or sortclause expression to other expressions we find in	 * the query, because arguments of ordinary operator expressions will be	 * cast that way.  (We have to do this for indexkeys because they are	 * represented without any explicit relabel in pg_index, and for sort	 * clauses because the parser is likewise cavalier about putting relabels	 * on them.)	 */	if (exprType((Node *) expr) != opcintype &&		!IsPolymorphicType(opcintype))	{		/* Strip any existing RelabelType, and add a new one if needed */		while (expr && IsA(expr, RelabelType))			expr = (Expr *) ((RelabelType *) expr)->arg;		if (exprType((Node *) expr) != opcintype)			expr = (Expr *) makeRelabelType(expr,											opcintype,											-1,											COERCE_DONTCARE);	}	/* Now find or create a matching EquivalenceClass */	eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,									  sortref);	/* And finally we can find or create a PathKey node */	if (canonicalize)		return make_canonical_pathkey(root, eclass, opfamily,									  strategy, nulls_first);	else		return makePathKey(eclass, opfamily, strategy, nulls_first);}/**************************************************************************** *		PATHKEY COMPARISONS ****************************************************************************//* * compare_pathkeys *	  Compare two pathkeys to see if they are equivalent, and if not whether *	  one is "better" than the other. * *	  This function may only be applied to canonicalized pathkey lists. *	  In the canonical representation, pathkeys can be checked for equality *	  by simple pointer comparison. */PathKeysComparisoncompare_pathkeys(List *keys1, List *keys2){	ListCell   *key1,			   *key2;	forboth(key1, keys1, key2, keys2)	{		PathKey    *pathkey1 = (PathKey *) lfirst(key1);		PathKey    *pathkey2 = (PathKey *) lfirst(key2);		/*		 * XXX would like to check that we've been given canonicalized input,		 * but PlannerInfo not accessible here...		 */#ifdef NOT_USED		Assert(list_member_ptr(root->canon_pathkeys, pathkey1));		Assert(list_member_ptr(root->canon_pathkeys, pathkey2));#endif		if (pathkey1 != pathkey2)			return PATHKEYS_DIFFERENT;	/* no need to keep looking */	}	/*	 * If we reached the end of only one list, the other is longer and	 * therefore not a subset.	 */	if (key1 == NULL && key2 == NULL)		return PATHKEYS_EQUAL;	if (key1 != NULL)		return PATHKEYS_BETTER1;	/* key1 is longer */	return PATHKEYS_BETTER2;	/* key2 is longer */}/* * pathkeys_contained_in *	  Common special case of compare_pathkeys: we just want to know *	  if keys2 are at least as well sorted as keys1. */boolpathkeys_contained_in(List *keys1, List *keys2){	switch (compare_pathkeys(keys1, keys2))	{		case PATHKEYS_EQUAL:		case PATHKEYS_BETTER2:			return true;		default:			break;	}	return false;}/* * get_cheapest_path_for_pathkeys *	  Find the cheapest path (according to the specified criterion) that *	  satisfies the given pathkeys.  Return NULL if no such path. * * 'paths' is a list of possible paths that all generate the same relation * 'pathkeys' represents a required ordering (already canonicalized!) * 'cost_criterion' is STARTUP_COST or TOTAL_COST */Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,							   CostSelector cost_criterion){	Path	   *matched_path = NULL;	ListCell   *l;	foreach(l, paths)	{		Path	   *path = (Path *) lfirst(l);		/*		 * Since cost comparison is a lot cheaper than pathkey comparison, do		 * that first.	(XXX is that still true?)		 */		if (matched_path != NULL &&			compare_path_costs(matched_path, path, cost_criterion) <= 0)			continue;		if (pathkeys_contained_in(pathkeys, path->pathkeys))			matched_path = path;	}	return matched_path;}/* * get_cheapest_fractional_path_for_pathkeys *	  Find the cheapest path (for retrieving a specified fraction of all *	  the tuples) that satisfies the given pathkeys. *	  Return NULL if no such path. * * See compare_fractional_path_costs() for the interpretation of the fraction * parameter. * * 'paths' is a list of possible paths that all generate the same relation * 'pathkeys' represents a required ordering (already canonicalized!) * 'fraction' is the fraction of the total tuples expected to be retrieved */Path *get_cheapest_fractional_path_for_pathkeys(List *paths,										  List *pathkeys,										  double fraction){	Path	   *matched_path = NULL;	ListCell   *l;	foreach(l, paths)	{		Path	   *path = (Path *) lfirst(l);		/*		 * Since cost comparison is a lot cheaper than pathkey comparison, do		 * that first.		 */		if (matched_path != NULL &&			compare_fractional_path_costs(matched_path, path, fraction) <= 0)			continue;		if (pathkeys_contained_in(pathkeys, path->pathkeys))			matched_path = path;	}	return matched_path;}/**************************************************************************** *		NEW PATHKEY FORMATION ****************************************************************************//* * build_index_pathkeys *	  Build a pathkeys list that describes the ordering induced by an index *	  scan using the given index.  (Note that an unordered index doesn't *	  induce any ordering; such an index will have no sortop OIDS in *	  its sortops arrays, and we will return NIL.) * * If 'scandir' is BackwardScanDirection, attempt to build pathkeys * representing a backwards scan of the index.	Return NIL if can't do it. * * The result is canonical, meaning that redundant pathkeys are removed; * it may therefore have fewer entries than there are index columns. * * We generate the full pathkeys list whether or not all are useful for the * current query.  Caller should do truncate_useless_pathkeys(). */List *build_index_pathkeys(PlannerInfo *root,					 IndexOptInfo *index,					 ScanDirection scandir){	List	   *retval = NIL;	ListCell   *indexprs_item = list_head(index->indexprs);	int			i;	for (i = 0; i < index->ncolumns; i++)	{		Oid			sortop;		bool		nulls_first;		int			ikey;		Expr	   *indexkey;		PathKey    *cpathkey;		if (ScanDirectionIsBackward(scandir))		{			sortop = index->revsortop[i];			nulls_first = !index->nulls_first[i];		}		else

⌨️ 快捷键说明

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