📄 indxpath.c
字号:
/*------------------------------------------------------------------------- * * indxpath.c * Routines to determine which indexes are usable for scanning a * given relation, and create Paths accordingly. * * 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/path/indxpath.c,v 1.191.2.8 2006/05/18 19:56:56 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <math.h>#include "access/skey.h"#include "catalog/pg_opclass.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"#include "optimizer/predtest.h"#include "optimizer/restrictinfo.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/pg_locale.h"#include "utils/selfuncs.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)#define IsBooleanOpclass(opclass) \ ((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID)static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, List *clauses, List *outer_clauses, bool istoplevel, bool isjoininner, Relids outer_relids);static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths);static int bitmap_path_comparator(const void *a, const void *b);static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths);static List *pull_indexpath_quals(Path *bitmapqual);static bool lists_intersect_ptr(List *list1, List *list2);static bool match_clause_to_indexcol(IndexOptInfo *index, int indexcol, Oid opclass, RestrictInfo *rinfo, Relids outer_relids);static Oid indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left);static Relids indexable_outerrelids(RelOptInfo *rel);static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids);static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel, Relids outer_relids, bool isouterjoin);static ScanDirection match_variant_ordering(PlannerInfo *root, IndexOptInfo *index, List *restrictclauses);static List *identify_ignorable_ordering_cols(PlannerInfo *root, IndexOptInfo *index, List *restrictclauses);static bool match_index_to_query_keys(PlannerInfo *root, IndexOptInfo *index, ScanDirection indexscandir, List *ignorables);static bool match_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index);static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left);static Expr *expand_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index);static List *expand_indexqual_condition(RestrictInfo *rinfo, 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, or have a predicate that * matches the query's qual 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 * * Note: check_partial_indexes() must have been run previously. */voidcreate_index_paths(PlannerInfo *root, RelOptInfo *rel){ List *indexpaths; List *bitindexpaths; ListCell *l; /* Skip the whole mess if no indexes */ if (rel->indexlist == NIL) { rel->index_outer_relids = NULL; return; } /* * Examine join clauses to see which ones are potentially usable with * indexes of this rel, 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. */ rel->index_outer_relids = indexable_outerrelids(rel); /* * Find all the index paths that are directly usable for this relation * (ie, are valid without considering OR or JOIN clauses). */ indexpaths = find_usable_indexes(root, rel, rel->baserestrictinfo, NIL, true, false, NULL); /* * We can submit them all to add_path. (This generates access paths for * plain IndexScan plans.) However, for the next step we will only want * the ones that have some selectivity; we must discard anything that was * generated solely for ordering purposes. */ bitindexpaths = NIL; foreach(l, indexpaths) { IndexPath *ipath = (IndexPath *) lfirst(l); add_path(rel, (Path *) ipath); if (ipath->indexselectivity < 1.0 && !ScanDirectionIsBackward(ipath->indexscandir)) bitindexpaths = lappend(bitindexpaths, ipath); } /* * Generate BitmapOrPaths for any suitable OR-clauses present in the * restriction list. Add these to bitindexpaths. */ indexpaths = generate_bitmap_or_paths(root, rel, rel->baserestrictinfo, NIL, false, NULL); bitindexpaths = list_concat(bitindexpaths, indexpaths); /* * If we found anything usable, generate a BitmapHeapPath for the most * promising combination of bitmap index paths. */ if (bitindexpaths != NIL) { Path *bitmapqual; BitmapHeapPath *bpath; bitmapqual = choose_bitmap_and(root, rel, bitindexpaths); bpath = create_bitmap_heap_path(root, rel, bitmapqual, false); add_path(rel, (Path *) bpath); }}/*---------- * find_usable_indexes * Given a list of restriction clauses, find all the potentially usable * indexes for the given relation, and return a list of IndexPaths. * * The caller actually supplies two lists of restriction clauses: some * "current" ones and some "outer" ones. Both lists can be used freely * to match keys of the index, but an index must use at least one of the * "current" clauses to be considered usable. The motivation for this is * examples like * WHERE (x = 42) AND (... OR (y = 52 AND z = 77) OR ....) * While we are considering the y/z subclause of the OR, we can use "x = 42" * as one of the available index conditions; but we shouldn't match the * subclause to any index on x alone, because such a Path would already have * been generated at the upper level. So we could use an index on x,y,z * or an index on x,y for the OR subclause, but not an index on just x. * When dealing with a partial index, a match of the index predicate to * one of the "current" clauses also makes the index usable. * * If istoplevel is true (indicating we are considering the top level of a * rel's restriction clauses), we will include indexes in the result that * have an interesting sort order, even if they have no matching restriction * clauses. * * 'rel' is the relation for which we want to generate index paths * 'clauses' is the current list of clauses (RestrictInfo nodes) * 'outer_clauses' is the list of additional upper-level clauses * 'istoplevel' is true if clauses are the rel's top-level restriction list * (outer_clauses must be NIL when this is true) * 'isjoininner' is true if forming an inner indexscan (so some of the * given clauses are join clauses) * 'outer_relids' identifies the outer side of the join (pass NULL * if not isjoininner) * * Note: check_partial_indexes() must have been run previously. *---------- */static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, List *clauses, List *outer_clauses, bool istoplevel, bool isjoininner, Relids outer_relids){ List *result = NIL; List *all_clauses = NIL; /* not computed till needed */ ListCell *ilist; foreach(ilist, rel->indexlist) { IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); IndexPath *ipath; List *restrictclauses; List *index_pathkeys; List *useful_pathkeys; bool useful_predicate; bool found_clause; bool index_is_ordered; /* * Ignore partial indexes that do not match the query. If a partial * index is marked predOK then we know it's OK; otherwise, if we are * at top level we know it's not OK (since predOK is exactly whether * its predicate could be proven from the toplevel clauses). * Otherwise, we have to test whether the added clauses are sufficient * to imply the predicate. If so, we could use the index in the * current context. * * We set useful_predicate to true iff the predicate was proven using * the current set of clauses. This is needed to prevent matching a * predOK index to an arm of an OR, which would be a legal but * pointlessly inefficient plan. (A better plan will be generated by * just scanning the predOK index alone, no OR.) */ useful_predicate = false; if (index->indpred != NIL) { if (index->predOK) { if (istoplevel) { /* we know predicate was proven from these clauses */ useful_predicate = true; } } else { if (istoplevel) continue; /* no point in trying to prove it */ /* Form all_clauses if not done already */ if (all_clauses == NIL) all_clauses = list_concat(list_copy(clauses), outer_clauses); if (!predicate_implied_by(index->indpred, all_clauses)) continue; /* can't use it at all */ if (!predicate_implied_by(index->indpred, outer_clauses)) useful_predicate = true; } } /* * 1. Match the index against the available restriction clauses. * found_clause is set true only if at least one of the current * clauses was used. */ restrictclauses = group_clauses_by_indexkey(index, clauses, outer_clauses, outer_relids, &found_clause); /* * Not all index AMs support scans with no restriction clauses. We * can't generate a scan over an index with amoptionalkey = false * unless there's at least one restriction clause. */ if (restrictclauses == NIL && !index->amoptionalkey) continue; /* * 2. Compute pathkeys describing index's ordering, if any, then see * how many of them are actually useful for this query. This is not * relevant unless we are at top level. */ index_is_ordered = OidIsValid(index->ordering[0]); if (istoplevel && index_is_ordered && !isjoininner) { index_pathkeys = build_index_pathkeys(root, index, ForwardScanDirection, true); useful_pathkeys = truncate_useless_pathkeys(root, rel, index_pathkeys); } else useful_pathkeys = NIL; /* * 3. Generate an indexscan path if there are relevant restriction * clauses in the current clauses, OR the index ordering is * potentially useful for later merging or final output ordering, OR * the index has a predicate that was proven by the current clauses. */ if (found_clause || useful_pathkeys != NIL || useful_predicate) { ipath = create_index_path(root, index, restrictclauses, useful_pathkeys, index_is_ordered ? ForwardScanDirection : NoMovementScanDirection, isjoininner); result = lappend(result, ipath); } /* * 4. If the index is ordered, and there is a requested query ordering * that we failed to match, consider variant ways of achieving the * ordering. Again, this is only interesting at top level. */ if (istoplevel && index_is_ordered && !isjoininner && root->query_pathkeys != NIL && pathkeys_useful_for_ordering(root, useful_pathkeys) == 0) { ScanDirection scandir; scandir = match_variant_ordering(root, index, restrictclauses); if (!ScanDirectionIsNoMovement(scandir)) { ipath = create_index_path(root, index, restrictclauses, root->query_pathkeys, scandir, false); result = lappend(result, ipath); } } } return result;}/* * generate_bitmap_or_paths * Look through the list of clauses to find OR clauses, and generate * a BitmapOrPath for each one we can handle that way. Return a list * of the generated BitmapOrPaths. * * outer_clauses is a list of additional clauses that can be assumed true * for the purpose of generating indexquals, but are not to be searched for * ORs. (See find_usable_indexes() for motivation.) */List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, List *clauses, List *outer_clauses, bool isjoininner, Relids outer_relids){ List *result = NIL; List *all_clauses; ListCell *l; /* * We can use both the current and outer clauses as context for * find_usable_indexes */ all_clauses = list_concat(list_copy(clauses), outer_clauses); foreach(l, clauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); List *pathlist; Path *bitmapqual; ListCell *j; Assert(IsA(rinfo, RestrictInfo)); /* Ignore RestrictInfos that aren't ORs */ if (!restriction_is_or_clause(rinfo)) continue; /* * We must be able to match at least one index to each of the arms of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -