📄 indxpath.c
字号:
} while (!DoneMatchingIndexKeys(classes)); if (!*found_clause && !found_outer_clause) return NIL; /* no indexable clauses anywhere */ return clausegroup_list;}/* * match_clause_to_indexcol() * Determines whether a restriction clause matches a column of an index. * * To match a normal index, the clause: * * (1) must be in the form (indexkey op const) or (const op indexkey); * and * (2) must contain an operator which is in the same class as the index * operator for this column, or is a "special" operator as recognized * by match_special_index_operator(). * * Our definition of "const" is pretty liberal: we allow Vars belonging * to the caller-specified outer_relids relations (which had better not * include the relation whose index is being tested). outer_relids should * be NULL when checking simple restriction clauses, and the outer side * of the join when building a join inner scan. Other than that, the * only thing we don't like is volatile functions. * * Note: in most cases we already know that the clause as a whole uses * vars from the interesting set of relations. The reason for the * outer_relids test is to reject clauses like (a.f1 OP (b.f2 OP a.f3)); * that's not processable by an indexscan nestloop join on A, whereas * (a.f1 OP (b.f2 OP c.f3)) is. * * Presently, the executor can only deal with indexquals that have the * indexkey on the left, so we can only use clauses that have the indexkey * on the right if we can commute the clause to put the key on the left. * We do not actually do the commuting here, but we check whether a * suitable commutator operator is available. * * For boolean indexes, it is also possible to match the clause directly * to the indexkey; or perhaps the clause is (NOT indexkey). * * 'index' is the index of interest. * 'indexcol' is a column number of 'index' (counting from 0). * 'opclass' is the corresponding operator class. * 'rinfo' is the clause to be tested (as a RestrictInfo node). * * Returns true if the clause can be used with this index key. * * NOTE: returns false if clause is an OR or AND clause; it is the * responsibility of higher-level routines to cope with those. */static boolmatch_clause_to_indexcol(IndexOptInfo *index, int indexcol, Oid opclass, RestrictInfo *rinfo, Relids outer_relids){ Expr *clause = rinfo->clause; Node *leftop, *rightop; /* First check for boolean-index cases. */ if (IsBooleanOpclass(opclass)) { if (match_boolean_index_clause((Node *) clause, indexcol, index)) return true; } /* Else clause must be a binary opclause. */ if (!is_opclause(clause)) return false; leftop = get_leftop(clause); rightop = get_rightop(clause); if (!leftop || !rightop) return false; /* * Check for clauses of the form: (indexkey operator constant) or * (constant operator indexkey). See above notes about const-ness. */ if (match_index_to_operand(leftop, indexcol, index) && bms_is_subset(rinfo->right_relids, outer_relids) && !contain_volatile_functions(rightop)) { if (is_indexable_operator(clause, opclass, true)) return true; /* * If we didn't find a member of the index's opclass, see whether it * is a "special" indexable operator. */ if (match_special_index_operator(clause, opclass, true)) return true; return false; } if (match_index_to_operand(rightop, indexcol, index) && bms_is_subset(rinfo->left_relids, outer_relids) && !contain_volatile_functions(leftop)) { if (is_indexable_operator(clause, opclass, false)) return true; /* * If we didn't find a member of the index's opclass, see whether it * is a "special" indexable operator. */ if (match_special_index_operator(clause, opclass, false)) return true; return false; } return false;}/* * indexable_operator * Does a binary opclause contain an operator matching the index opclass? * * If the indexkey is on the right, what we actually want to know * is whether the operator has a commutator operator that matches * the index's opclass. * * Returns the OID of the matching operator, or InvalidOid if no match. * (Formerly, this routine might return a binary-compatible operator * rather than the original one, but that kluge is history.) */static Oidindexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left){ Oid expr_op = ((OpExpr *) clause)->opno; Oid commuted_op; /* Get the commuted operator if necessary */ if (indexkey_on_left) commuted_op = expr_op; else commuted_op = get_commutator(expr_op); if (commuted_op == InvalidOid) return InvalidOid; /* OK if the (commuted) operator is a member of the index's opclass */ if (op_in_opclass(commuted_op, opclass)) return expr_op; return InvalidOid;}/**************************************************************************** * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- ****************************************************************************//* * check_partial_indexes * Check each partial index of the relation, and mark it predOK or not * depending on whether the predicate is satisfied for this query. */voidcheck_partial_indexes(PlannerInfo *root, RelOptInfo *rel){ List *restrictinfo_list = rel->baserestrictinfo; ListCell *ilist; /* * Note: if Postgres tried to optimize queries by forming equivalence * classes over equi-joined attributes (i.e., if it recognized that a * qualification such as "where a.b=c.d and a.b=5" could make use of an * index on c.d), then we could use that equivalence class info here with * joininfo lists to do more complete tests for the usability of a partial * index. For now, the test only uses restriction clauses (those in * baserestrictinfo). --Nels, Dec '92 * * XXX as of 7.1, equivalence class info *is* available. Consider * improving this code as foreseen by Nels. */ foreach(ilist, rel->indexlist) { IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); if (index->indpred == NIL) continue; /* ignore non-partial indexes */ index->predOK = predicate_implied_by(index->indpred, restrictinfo_list); }}/**************************************************************************** * ---- ROUTINES TO CHECK JOIN CLAUSES ---- ****************************************************************************//* * indexable_outerrelids * Finds all other relids that participate in any indexable join clause * for the specified table. Returns a set of relids. */static Relidsindexable_outerrelids(RelOptInfo *rel){ Relids outer_relids = NULL; ListCell *l; /* * Examine each joinclause in the joininfo list to see if it matches any * key of any index. If so, add the clause's other rels to the result. */ foreach(l, rel->joininfo) { RestrictInfo *joininfo = (RestrictInfo *) lfirst(l); Relids other_rels; other_rels = bms_difference(joininfo->required_relids, rel->relids); if (matches_any_index(joininfo, rel, other_rels)) outer_relids = bms_join(outer_relids, other_rels); else bms_free(other_rels); } return outer_relids;}/* * matches_any_index * Workhorse for indexable_outerrelids: see if a joinclause can be * matched to any index of the given rel. */static boolmatches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids){ ListCell *l; Assert(IsA(rinfo, RestrictInfo)); if (restriction_is_or_clause(rinfo)) { foreach(l, ((BoolExpr *) rinfo->orclause)->args) { Node *orarg = (Node *) lfirst(l); /* OR arguments should be ANDs or sub-RestrictInfos */ if (and_clause(orarg)) { ListCell *j; /* Recurse to examine AND items and sub-ORs */ foreach(j, ((BoolExpr *) orarg)->args) { RestrictInfo *arinfo = (RestrictInfo *) lfirst(j); if (matches_any_index(arinfo, rel, outer_relids)) return true; } } else { /* Recurse to examine simple clause */ Assert(IsA(orarg, RestrictInfo)); Assert(!restriction_is_or_clause((RestrictInfo *) orarg)); if (matches_any_index((RestrictInfo *) orarg, rel, outer_relids)) return true; } } return false; } /* Normal case for a simple restriction clause */ foreach(l, rel->indexlist) { IndexOptInfo *index = (IndexOptInfo *) lfirst(l); int indexcol = 0; Oid *classes = index->classlist; do { Oid curClass = classes[0]; if (match_clause_to_indexcol(index, indexcol, curClass, rinfo, outer_relids)) return true; indexcol++; classes++; } while (!DoneMatchingIndexKeys(classes)); } return false;}/* * best_inner_indexscan * Finds the best available inner indexscan for a nestloop join * with the given rel on the inside and the given outer_relids outside. * May return NULL if there are no possible inner indexscans. * * We ignore ordering considerations (since a nestloop's inner scan's order * is uninteresting). Also, we consider only total cost when deciding which * of two possible paths is better --- this assumes that all indexpaths have * negligible startup cost. (True today, but someday we might have to think * harder.) Therefore, there is only one dimension of comparison and so it's * sufficient to return a single "best" path. */Path *best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel, Relids outer_relids, JoinType jointype){ Path *cheapest; bool isouterjoin; List *clause_list; List *indexpaths; List *bitindexpaths; ListCell *l; InnerIndexscanInfo *info; MemoryContext oldcontext; /* * Nestloop only supports inner, left, and IN joins. */ switch (jointype) { case JOIN_INNER: case JOIN_IN: case JOIN_UNIQUE_OUTER: isouterjoin = false; break; case JOIN_LEFT: isouterjoin = true; break; default: return NULL; } /* * If there are no indexable joinclauses for this rel, exit quickly. */ if (bms_is_empty(rel->index_outer_relids)) return NULL; /* * Otherwise, we have to do path selection in the memory context of the * given rel, so that any created path can be safely attached to the rel's * cache of best inner paths. (This is not currently an issue for normal * planning, but it is an issue for GEQO planning.) */ oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(rel)); /* * Intersect the given outer_relids with index_outer_relids to find the * set of outer relids actually relevant for this rel. If there are none, * again we can fail immediately. */ outer_relids = bms_intersect(rel->index_outer_relids, outer_relids); if (bms_is_empty(outer_relids)) { bms_free(outer_relids); MemoryContextSwitchTo(oldcontext); return NULL; } /* * Look to see if we already computed the result for this set of relevant * outerrels. (We include the isouterjoin status in the cache lookup key * for safety. In practice I suspect this is not necessary because it * should always be the same for a given innerrel.) */ foreach(l, rel->index_inner_paths) { info = (InnerIndexscanInfo *) lfirst(l); if (bms_equal(info->other_relids, outer_relids) && info->isouterjoin == isouterjoin) { bms_free(outer_relids); MemoryContextSwitchTo(oldcontext); return info->best_innerpath; } } /* * Find all the relevant restriction and join clauses. * * Note: because we include restriction clauses, we will find indexscans * that could be plain indexscans, ie, they don't require the join context * at all. This may seem redundant, but we need to include those scans in * the input given to choose_bitmap_and() to be sure we find optimal AND * combinations of join and non-join scans. The worst case is that we * might return a "best inner indexscan" that's really just a plain * indexscan, causing some redundant effort in joinpath.c. */ clause_list = find_clauses_for_join(root, rel, outer_relids, isouterjoin); /* * Find all the index paths that are usable for this join, except for * stuff involving OR clauses. */ indexpaths = find_usable_indexes(root, rel, clause_list, NIL, false, true, outer_relids); /* * Generate BitmapOrPaths for any suitable OR-clauses present in the * clause list. */ bitindexpaths = generate_bitmap_or_paths(root, rel, clause_list, NIL, true, outer_relids); /* * Include the regular index paths in bitindexpaths. */ bitindexpaths = list_concat(bitindexpaths, list_copy(indexpaths)); /* * If we found anything usable, generate a BitmapHeapPath for the most
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -