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 + -
显示快捷键?