indxpath.c

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

C
2,156
字号
/*------------------------------------------------------------------------- * * indxpath.c *	  Routines to determine which indices are usable for scanning a *	  given relation, and create IndexPaths accordingly. * * 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/path/indxpath.c,v 1.147 2003/08/04 02:40:00 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <math.h>#include "access/nbtree.h"#include "catalog/pg_amop.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "executor/executor.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"#include "optimizer/restrictinfo.h"#include "optimizer/var.h"#include "parser/parse_expr.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/catcache.h"#include "utils/lsyscache.h"#include "utils/pg_locale.h"#include "utils/selfuncs.h"#include "utils/syscache.h"/* * DoneMatchingIndexKeys() - MACRO */#define DoneMatchingIndexKeys(classes)	(classes[0] == InvalidOid)#define is_indexable_operator(clause,opclass,indexkey_on_left) \	(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,					  List *restrictinfo_list);static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,					 List *or_clauses,					 List *other_matching_indices);static bool match_or_subclause_to_indexkey(RelOptInfo *rel,							   IndexOptInfo *index,							   Expr *clause);static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);static List *group_clauses_by_indexkey_for_join(Query *root,								   RelOptInfo *rel, IndexOptInfo *index,								   Relids outer_relids,								   JoinType jointype, bool isouterjoin);static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,						 int indexcol, Oid opclass, Expr *clause);static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,							  int indexcol, Oid opclass, Expr *clause);static Oid indexable_operator(Expr *clause, Oid opclass,				   bool indexkey_on_left);static bool pred_test(List *predicate_list, List *restrictinfo_list,		  List *joininfo_list);static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);static bool pred_test_recurse_clause(Expr *predicate, Node *clause);static bool pred_test_recurse_pred(Expr *predicate, Node *clause);static bool pred_test_simple_clause(Expr *predicate, Node *clause);static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);static Path *make_innerjoin_index_path(Query *root,						  RelOptInfo *rel, IndexOptInfo *index,						  List *clausegroups);static bool match_index_to_operand(Node *operand, int indexcol,					   RelOptInfo *rel, IndexOptInfo *index);static bool match_special_index_operator(Expr *clause, Oid opclass,							 bool indexkey_on_left);static List *expand_indexqual_condition(Expr *clause, Oid opclass);static List *prefix_quals(Node *leftop, Oid opclass,			 Const *prefix, Pattern_Prefix_Status pstatus);static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,					 Datum rightop);static Datum string_to_datum(const char *str, Oid datatype);static Const *string_to_const(const char *str, Oid datatype);/* * create_index_paths() *	  Generate all interesting index paths for the given relation. *	  Candidate paths are added to the rel's pathlist (using add_path). * * To be considered for an index scan, an index must match one or more * restriction clauses or join clauses from the query's qual condition, * or match the query's ORDER BY condition. * * There are two basic kinds of index scans.  A "plain" index scan uses * only restriction clauses (possibly none at all) in its indexqual, * so it can be applied in any context.  An "innerjoin" index scan uses * join clauses (plus restriction clauses, if available) in its indexqual. * Therefore it can only be used as the inner relation of a nestloop * join against an outer rel that includes all the other rels mentioned * in its join clauses.  In that context, values for the other rels' * attributes are available and fixed during any one scan of the indexpath. * * An IndexPath is generated and submitted to add_path() for each plain index * scan this routine deems potentially interesting for the current query. * * We also determine the set of other relids that participate in join * clauses that could be used with each index.	The actually best innerjoin * path will be generated for each outer relation later on, but knowing the * set of potential otherrels allows us to identify equivalent outer relations * and avoid repeated computation. * * 'rel' is the relation for which we want to generate index paths */voidcreate_index_paths(Query *root, RelOptInfo *rel){	List	   *restrictinfo_list = rel->baserestrictinfo;	List	   *joininfo_list = rel->joininfo;	Relids		all_join_outerrelids = NULL;	List	   *ilist;	foreach(ilist, rel->indexlist)	{		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);		List	   *restrictclauses;		List	   *index_pathkeys;		List	   *useful_pathkeys;		bool		index_is_ordered;		Relids		join_outerrelids;		/*		 * If this is a partial index, we can only use it if it passes the		 * predicate test.		 */		if (index->indpred != NIL)			if (!pred_test(index->indpred, restrictinfo_list, joininfo_list))				continue;		/*		 * 1. Try matching the index against subclauses of restriction		 * 'or' clauses (ie, 'or' clauses that reference only this		 * relation). The restrictinfo nodes for the 'or' clauses are		 * marked with lists of the matching indices.  No paths are		 * actually created now; that will be done in orindxpath.c after		 * all indexes for the rel have been examined.	(We need to do it		 * that way because we can potentially use a different index for		 * each subclause of an 'or', so we can't build a path for an 'or'		 * clause until all indexes have been matched against it.)		 *		 * We don't even think about special handling of 'or' clauses that		 * involve more than one relation (ie, are join clauses). Can we		 * do anything useful with those?		 */		match_index_orclauses(rel, index, restrictinfo_list);		/*		 * 2. Match the index against non-'or' restriction clauses.		 */		restrictclauses = group_clauses_by_indexkey(rel, index);		/*		 * 3. Compute pathkeys describing index's ordering, if any, then		 * see how many of them are actually useful for this query.		 */		index_pathkeys = build_index_pathkeys(root, rel, index,											  ForwardScanDirection);		index_is_ordered = (index_pathkeys != NIL);		useful_pathkeys = truncate_useless_pathkeys(root, rel,													index_pathkeys);		/*		 * 4. Generate an indexscan path if there are relevant restriction		 * clauses OR the index ordering is potentially useful for later		 * merging or final output ordering.		 *		 * If there is a predicate, consider it anyway since the index		 * predicate has already been found to match the query.  The		 * selectivity of the predicate might alone make the index useful.		 */		if (restrictclauses != NIL ||			useful_pathkeys != NIL ||			index->indpred != NIL)			add_path(rel, (Path *)					 create_index_path(root, rel, index,									   restrictclauses,									   useful_pathkeys,									   index_is_ordered ?									   ForwardScanDirection :									   NoMovementScanDirection));		/*		 * 5. If the index is ordered, a backwards scan might be		 * interesting. Currently this is only possible for a DESC query		 * result ordering.		 */		if (index_is_ordered)		{			index_pathkeys = build_index_pathkeys(root, rel, index,												  BackwardScanDirection);			useful_pathkeys = truncate_useless_pathkeys(root, rel,														index_pathkeys);			if (useful_pathkeys != NIL)				add_path(rel, (Path *)						 create_index_path(root, rel, index,										   restrictclauses,										   useful_pathkeys,										   BackwardScanDirection));		}		/*		 * 6. Examine join clauses to see which ones are potentially		 * usable with this index, and generate the set of all other		 * relids that participate in such join clauses.  We'll use this		 * set later to recognize outer rels that are equivalent for		 * joining purposes. We compute both per-index and		 * overall-for-relation sets.		 */		join_outerrelids = indexable_outerrelids(rel, index);		index->outer_relids = join_outerrelids;		all_join_outerrelids = bms_add_members(all_join_outerrelids,											   join_outerrelids);	}	rel->index_outer_relids = all_join_outerrelids;}/**************************************************************************** *		----  ROUTINES TO PROCESS 'OR' CLAUSES	---- ****************************************************************************//* * match_index_orclauses *	  Attempt to match an index against subclauses within 'or' clauses. *	  Each subclause that does match is marked with the index's node. * *	  Essentially, this adds 'index' to the list of subclause indices in *	  the RestrictInfo field of each of the 'or' clauses where it matches. *	  NOTE: we can use storage in the RestrictInfo for this purpose because *	  this processing is only done on single-relation restriction clauses. *	  Therefore, we will never have indexes for more than one relation *	  mentioned in the same RestrictInfo node's list. * * 'rel' is the node of the relation on which the index is defined. * 'index' is the index node. * 'restrictinfo_list' is the list of available restriction clauses. */static voidmatch_index_orclauses(RelOptInfo *rel,					  IndexOptInfo *index,					  List *restrictinfo_list){	List	   *i;	foreach(i, restrictinfo_list)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);		if (restriction_is_or_clause(restrictinfo))		{			/*			 * Add this index to the subclause index list for each			 * subclause that it matches.			 */			restrictinfo->subclauseindices =				match_index_orclause(rel, index,							   ((BoolExpr *) restrictinfo->clause)->args,									 restrictinfo->subclauseindices);		}	}}/* * match_index_orclause *	  Attempts to match an index against the subclauses of an 'or' clause. * *	  A match means that: *	  (1) the operator within the subclause can be used with the *		  index's specified operator class, and *	  (2) one operand of the subclause matches the index key. * *	  If a subclause is an 'and' clause, then it matches if any of its *	  subclauses is an opclause that matches. * * 'or_clauses' is the list of subclauses within the 'or' clause * 'other_matching_indices' is the list of information on other indices *		that have already been matched to subclauses within this *		particular 'or' clause (i.e., a list previously generated by *		this routine), or NIL if this routine has not previously been *		run for this 'or' clause. * * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where * a,b,c are nodes of indices that match the first subclause in * 'or-clauses', d,e,f match the second subclause, no indices * match the third, g,h match the fourth, etc. */static List *match_index_orclause(RelOptInfo *rel,					 IndexOptInfo *index,					 List *or_clauses,					 List *other_matching_indices){	List	   *matching_indices;	List	   *index_list;	List	   *clist;	/*	 * first time through, we create list of same length as OR clause,	 * containing an empty sublist for each subclause.	 */	if (!other_matching_indices)	{		matching_indices = NIL;		foreach(clist, or_clauses)			matching_indices = lcons(NIL, matching_indices);	}	else		matching_indices = other_matching_indices;	index_list = matching_indices;	foreach(clist, or_clauses)	{		Expr	   *clause = lfirst(clist);		if (match_or_subclause_to_indexkey(rel, index, clause))		{			/* OK to add this index to sublist for this subclause */			lfirst(matching_indices) = lcons(index,											 lfirst(matching_indices));		}		matching_indices = lnext(matching_indices);	}	return index_list;}/* * See if a subclause of an OR clause matches an index. * * We accept the subclause if it is an operator clause that matches the * index, or if it is an AND clause any of whose members is an opclause * that matches the index. * * For multi-key indexes, we only look for matches to the first key; * without such a match the index is useless.  If the clause is an AND * then we may be able to extract additional subclauses to use with the * later indexkeys, but we need not worry about that until * extract_or_indexqual_conditions() is called (if it ever is). */static boolmatch_or_subclause_to_indexkey(RelOptInfo *rel,							   IndexOptInfo *index,							   Expr *clause){	Oid			opclass = index->classlist[0];	if (and_clause((Node *) clause))	{		List	   *item;		foreach(item, ((BoolExpr *) clause)->args)		{			if (match_clause_to_indexcol(rel, index, 0, opclass,										 lfirst(item)))				return true;		}		return false;	}	else		return match_clause_to_indexcol(rel, index, 0, opclass,										clause);}/*---------- * Given an OR subclause that has previously been determined to match * the specified index, extract a list of specific opclauses that can be * used as indexquals. * * In the simplest case this just means making a one-element list of the * given opclause.	However, if the OR subclause is an AND, we have to * scan it to find the opclause(s) that match the index.  (There should * be at least one, if match_or_subclause_to_indexkey succeeded, but there * could be more.) * * Also, we can look at other restriction clauses of the rel to discover * additional candidate indexquals: for example, consider *			... where (a = 11 or a = 12) and b = 42; * If we are dealing with an index on (a,b) then we can include the clause * b = 42 in the indexqual list generated for each of the OR subclauses. * Essentially, we are making an index-specific transformation from CNF to * DNF.  (NOTE: when we do this, we end up with a slightly inefficient plan * because create_indexscan_plan is not very bright about figuring out which * restriction clauses are implied by the generated indexqual condition. * Currently we'll end up rechecking both the OR clause and the transferred * restriction clause as qpquals.  FIXME someday.) * * Also, we apply expand_indexqual_condition() to convert any special * matching opclauses to indexable operators. * * The passed-in clause is not changed. *---------- */List *extract_or_indexqual_conditions(RelOptInfo *rel,								IndexOptInfo *index,								Expr *orsubclause){	FastList	quals;	int			indexcol = 0;	Oid		   *classes = index->classlist;	FastListInit(&quals);	/*	 * Extract relevant indexclauses in indexkey order.  This is	 * essentially just like group_clauses_by_indexkey() except that the	 * input and output are lists of bare clauses, not of RestrictInfo	 * nodes, and that we expand special operators immediately.	 */

⌨️ 快捷键说明

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