diff options
Diffstat (limited to 'src/backend/optimizer/path/indxpath.c')
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 1998 |
1 files changed, 1047 insertions, 951 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 11ee2317376..920593781b2 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -61,6 +61,14 @@ typedef enum ST_ANYSCAN /* either is okay */ } ScanTypeControl; +/* Data structure for collecting qual clauses that match an index */ +typedef struct +{ + bool nonempty; /* True if lists are not all empty */ + /* Lists of RestrictInfos, one per index column */ + List *indexclauses[INDEX_MAX_KEYS]; +} IndexClauseSet; + /* Per-path data used within choose_bitmap_and() */ typedef struct { @@ -71,55 +79,73 @@ typedef struct } PathClauseUsage; -static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, - List *clauses, List *outer_clauses, - bool istoplevel, RelOptInfo *outer_rel, - SaOpControl saop_control, ScanTypeControl scantype); -static List *find_saop_paths(PlannerInfo *root, RelOptInfo *rel, - List *clauses, List *outer_clauses, - bool istoplevel, RelOptInfo *outer_rel); +static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, + IndexClauseSet *rclauseset, + IndexClauseSet *jclauseset, + IndexClauseSet *eclauseset, + List **bitindexpaths); +static void expand_eclass_clause_combinations(PlannerInfo *root, + RelOptInfo *rel, + IndexOptInfo *index, + int thiscol, int lastcol, + IndexClauseSet *clauseset, + IndexClauseSet *eclauseset, + List **bitindexpaths); +static void get_index_paths(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, IndexClauseSet *clauses, + List **bitindexpaths); +static List *build_index_paths(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, IndexClauseSet *clauses, + bool useful_predicate, + SaOpControl saop_control, ScanTypeControl scantype); +static List *build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel, + List *clauses, List *other_clauses); +static List *drop_indexable_join_clauses(RelOptInfo *rel, List *clauses); static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, - List *paths, RelOptInfo *outer_rel); + List *paths); static int path_usage_comparator(const void *a, const void *b); static Cost bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, - Path *ipath, RelOptInfo *outer_rel); + Path *ipath); static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, - List *paths, RelOptInfo *outer_rel); + List *paths); static PathClauseUsage *classify_index_clause_usage(Path *path, List **clauselist); static void find_indexpath_quals(Path *bitmapqual, List **quals, List **preds); static int find_list_position(Node *node, List **nodelist); static bool check_index_only(RelOptInfo *rel, IndexOptInfo *index); +static double get_loop_count(PlannerInfo *root, Relids outer_relids); +static void match_restriction_clauses_to_index(RelOptInfo *rel, + IndexOptInfo *index, + IndexClauseSet *clauseset); +static void match_join_clauses_to_index(PlannerInfo *root, + RelOptInfo *rel, IndexOptInfo *index, + IndexClauseSet *clauseset, + List **joinorclauses); +static void match_eclass_clauses_to_index(PlannerInfo *root, + IndexOptInfo *index, + IndexClauseSet *clauseset); static void match_clauses_to_index(IndexOptInfo *index, - List *clauses, List *outer_clauses, - Relids outer_relids, - SaOpControl saop_control, - List **index_clauses_p, - List **clause_columns_p, - bool *found_clause); + List *clauses, + IndexClauseSet *clauseset); +static void match_clause_to_index(IndexOptInfo *index, + RestrictInfo *rinfo, + IndexClauseSet *clauseset); static bool match_clause_to_indexcol(IndexOptInfo *index, int indexcol, - RestrictInfo *rinfo, - Relids outer_relids, - SaOpControl saop_control); + RestrictInfo *rinfo); static bool is_indexable_operator(Oid expr_op, Oid opfamily, bool indexkey_on_left); static bool match_rowcompare_to_indexcol(IndexOptInfo *index, int indexcol, Oid opfamily, Oid idxcollation, - RowCompareExpr *clause, - Relids outer_relids); + RowCompareExpr *clause); static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, List **orderby_clauses_p, List **clause_columns_p); static Expr *match_clause_to_ordering_op(IndexOptInfo *index, int indexcol, Expr *clause, Oid pk_opfamily); -static Relids indexable_outerrelids(PlannerInfo *root, 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 bool match_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, @@ -152,21 +178,17 @@ static Const *string_to_const(const char *str, Oid datatype); * * 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 + * so it can be applied in any context. A "parameterized" 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' + * When joining such a scan to one of the relations supplying the other + * variables used in its indexqual, the parameterized scan must appear as + * the inner relation of a nestloop join; it can't be used on the outer side, + * nor in a merge or hash join. 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. + * An IndexPath is generated and submitted to add_path() for each plain or + * parameterized index scan this routine deems potentially interesting for + * the current query. * * 'rel' is the relation for which we want to generate index paths * @@ -177,98 +199,631 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) { List *indexpaths; List *bitindexpaths; - ListCell *l; + List *bitjoinpaths; + List *joinorclauses; + IndexClauseSet rclauseset; + IndexClauseSet jclauseset; + IndexClauseSet eclauseset; + ListCell *ilist; /* Skip the whole mess if no indexes */ if (rel->indexlist == NIL) - { - rel->index_outer_relids = NULL; return; + + /* Bitmap paths are collected and then dealt with at the end */ + bitindexpaths = bitjoinpaths = joinorclauses = NIL; + + /* Examine each index in turn */ + foreach(ilist, rel->indexlist) + { + IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); + + /* Protect limited-size array in IndexClauseSets */ + Assert(index->ncolumns <= INDEX_MAX_KEYS); + + /* + * Ignore partial indexes that do not match the query. + * (generate_bitmap_or_paths() might be able to do something with + * them, but that's of no concern here.) + */ + if (index->indpred != NIL && !index->predOK) + continue; + + /* + * Identify the restriction clauses that can match the index. + */ + MemSet(&rclauseset, 0, sizeof(rclauseset)); + match_restriction_clauses_to_index(rel, index, &rclauseset); + + /* + * Build index paths from the restriction clauses. These will be + * non-parameterized paths. Plain paths go directly to add_path(), + * bitmap paths are added to bitindexpaths to be handled below. + */ + get_index_paths(root, rel, index, &rclauseset, + &bitindexpaths); + + /* + * Identify the join clauses that can match the index. For the moment + * we keep them separate from the restriction clauses. Note that + * this finds only "loose" join clauses that have not been merged + * into EquivalenceClasses. Also, collect join OR clauses for later. + */ + MemSet(&jclauseset, 0, sizeof(jclauseset)); + match_join_clauses_to_index(root, rel, index, + &jclauseset, &joinorclauses); + + /* + * Look for EquivalenceClasses that can generate joinclauses + * matching the index. + */ + MemSet(&eclauseset, 0, sizeof(eclauseset)); + match_eclass_clauses_to_index(root, index, &eclauseset); + + /* + * If we found any plain or eclass join clauses, decide what to + * do with 'em. + */ + if (jclauseset.nonempty || eclauseset.nonempty) + consider_index_join_clauses(root, rel, index, + &rclauseset, + &jclauseset, + &eclauseset, + &bitjoinpaths); } /* - * 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. + * Generate BitmapOrPaths for any suitable OR-clauses present in the + * restriction list. Add these to bitindexpaths. */ - rel->index_outer_relids = indexable_outerrelids(root, rel); + indexpaths = generate_bitmap_or_paths(root, rel, + rel->baserestrictinfo, NIL, + false); + bitindexpaths = list_concat(bitindexpaths, indexpaths); + + /* + * Likewise, generate BitmapOrPaths for any suitable OR-clauses present in + * the joinclause list. Add these to bitjoinpaths. + */ + indexpaths = generate_bitmap_or_paths(root, rel, + joinorclauses, rel->baserestrictinfo, + false); + bitjoinpaths = list_concat(bitjoinpaths, indexpaths); + + /* + * If we found anything usable, generate a BitmapHeapPath for the most + * promising combination of restriction bitmap index paths. Note there + * will be only one such path no matter how many indexes exist. This + * should be sufficient since there's basically only one figure of merit + * (total cost) for such a path. + */ + if (bitindexpaths != NIL) + { + Path *bitmapqual; + BitmapHeapPath *bpath; + + bitmapqual = choose_bitmap_and(root, rel, bitindexpaths); + bpath = create_bitmap_heap_path(root, rel, bitmapqual, 1.0); + add_path(rel, (Path *) bpath); + } + + /* + * Likewise, if we found anything usable, generate a BitmapHeapPath for + * the most promising combination of join bitmap index paths. Note there + * will be only one such path no matter how many join clauses are + * available. (XXX is that good enough, or do we need to consider even + * more paths for different subsets of possible join partners? Also, + * should we add in restriction bitmap paths as well?) + */ + if (bitjoinpaths != NIL) + { + Path *bitmapqual; + double loop_count; + BitmapHeapPath *bpath; + + bitmapqual = choose_bitmap_and(root, rel, bitjoinpaths); + loop_count = get_loop_count(root, bitmapqual->required_outer); + bpath = create_bitmap_heap_path(root, rel, bitmapqual, loop_count); + add_path(rel, (Path *) bpath); + } +} + +/* + * consider_index_join_clauses + * Given sets of join clauses for an index, decide which parameterized + * index paths to build. + * + * Plain indexpaths are sent directly to add_path, while potential + * bitmap indexpaths are added to *bitindexpaths for later processing. + * + * 'rel' is the index's heap relation + * 'index' is the index for which we want to generate paths + * 'rclauseset' is the collection of indexable restriction clauses + * 'jclauseset' is the collection of indexable simple join clauses + * 'eclauseset' is the collection of indexable clauses from EquivalenceClasses + * '*bitindexpaths' is the list to add bitmap paths to + * + * Note: this changes the clause lists contained in the passed clausesets, + * but we don't care since the caller is done with them. + */ +static void +consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, + IndexClauseSet *rclauseset, + IndexClauseSet *jclauseset, + IndexClauseSet *eclauseset, + List **bitindexpaths) +{ + IndexClauseSet clauseset; + int last_eclass_col; + int indexcol; + + /* + * We can always include any restriction clauses in the index clauses. + * However, it's not obvious which subsets of the join clauses are worth + * generating paths from, and it's unlikely that considering every + * possible subset is worth the cycles. Our current heuristic is based + * on the index columns, with the idea that later index columns are less + * useful than earlier ones; therefore it's unlikely to be worth trying + * combinations that would remove a clause from an earlier index column + * while adding one to a later column. Also, we know that all the + * eclass clauses for a particular column are redundant, so we should + * use only one of them. However, eclass clauses will always represent + * equality which is the strongest type of index constraint, so those + * are high-value and we should try every available combination when we + * have eclass clauses for more than one column. Furthermore, it's + * unlikely to be useful to combine an eclass clause with non-eclass + * clauses for the same index column. These considerations lead to the + * following heuristics: + * + * First, start with the restriction clauses, and add on all simple join + * clauses for column 1. If there are any such join clauses, generate + * paths with this collection of clauses. Then, if there are eclass + * clauses for column 1, generate paths with each one of them replacing + * any other clauses we have for column 1. + * + * Next, add on all simple join clauses for column 2. If there are any + * such join clauses, generate paths with this collection. If there are + * eclass clauses for columns 1 or 2, generate paths with each such clause + * replacing other clauses for its index column, including cases where we + * use restriction or simple join clauses for one column and an eclass + * clause for the other. + * + * Repeat for each additional index column. + */ + + /* Set up working set with just the restriction clauses */ + memcpy(&clauseset, rclauseset, sizeof(clauseset)); + /* Even if it's empty right now, it won't be by the time we use it */ + clauseset.nonempty = true; + + last_eclass_col = -1; + for (indexcol = 0; indexcol < index->ncolumns; indexcol++) + { + /* + * If we don't have either simple join clauses or eclass clauses for + * this column, no new paths can be created in this iteration. + */ + if (jclauseset->indexclauses[indexcol] == NIL && + eclauseset->indexclauses[indexcol] == NIL) + continue; + + /* Add any simple join clauses to the working set */ + clauseset.indexclauses[indexcol] = + list_concat(clauseset.indexclauses[indexcol], + jclauseset->indexclauses[indexcol]); + + /* Set recursion depth to reach last col with eclass clauses */ + if (eclauseset->indexclauses[indexcol] != NIL) + last_eclass_col = indexcol; + + /* Do we have eclass clauses for any column now under consideration? */ + if (last_eclass_col >= 0) + { + /* Yes, so recursively generate all eclass clause combinations */ + expand_eclass_clause_combinations(root, rel, index, + 0, last_eclass_col, + &clauseset, eclauseset, + bitindexpaths); + } + else + { + /* No, consider the newly-enlarged set of simple join clauses */ + get_index_paths(root, rel, index, &clauseset, bitindexpaths); + } + } +} + +/* + * expand_eclass_clause_combinations + * Generate all combinations of eclass join clauses for first N columns, + * and construct parameterized index paths for each combination. + * + * Workhorse for consider_index_join_clauses; see notes therein for rationale. + * It's convenient to use recursion to implement the enumeration, since we + * can have at most INDEX_MAX_KEYS recursion levels. + * + * 'rel', 'index', 'eclauseset', 'bitindexpaths' as above + * 'thiscol' is the current index column number/recursion level + * 'lastcol' is the last index column we should consider eclass clauses for + * 'clauseset' is the current collection of indexable clauses + */ +static void +expand_eclass_clause_combinations(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, + int thiscol, int lastcol, + IndexClauseSet *clauseset, + IndexClauseSet *eclauseset, + List **bitindexpaths) +{ + List *save_clauses; + ListCell *lc; + + /* If past last eclass column, end the recursion and generate paths */ + if (thiscol > lastcol) + { + get_index_paths(root, rel, index, clauseset, bitindexpaths); + return; + } + + /* If no eclass clauses to consider for this column, just recurse */ + if (eclauseset->indexclauses[thiscol] == NIL) + { + Assert(thiscol < lastcol); + expand_eclass_clause_combinations(root, rel, index, + thiscol + 1, lastcol, + clauseset, eclauseset, + bitindexpaths); + return; + } + + /* We'll momentarily save and restore the list of non-eclass clauses */ + save_clauses = clauseset->indexclauses[thiscol]; + + /* If we have non-eclass clauses for this column, first try with those */ + if (save_clauses) + expand_eclass_clause_combinations(root, rel, index, + thiscol + 1, lastcol, + clauseset, eclauseset, + bitindexpaths); + + /* For each eclass clause alternative ... */ + foreach(lc, eclauseset->indexclauses[thiscol]) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + /* Replace any existing clauses with the eclass clause */ + clauseset->indexclauses[thiscol] = list_make1(rinfo); + + /* Recurse to advance to next column */ + expand_eclass_clause_combinations(root, rel, index, + thiscol + 1, lastcol, + clauseset, eclauseset, + bitindexpaths); + } + + /* Restore previous list contents */ + clauseset->indexclauses[thiscol] = save_clauses; +} + + +/* + * get_index_paths + * Given an index and a set of index clauses for it, construct IndexPaths. + * + * Plain indexpaths are sent directly to add_path, while potential + * bitmap indexpaths are added to *bitindexpaths for later processing. + * + * This is a fairly simple frontend to build_index_paths(). Its reason for + * existence is mainly to handle ScalarArrayOpExpr quals properly. If the + * index AM supports them natively, we should just include them in simple + * index paths. If not, we should exclude them while building simple index + * paths, and then make a separate attempt to include them in bitmap paths. + */ +static void +get_index_paths(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, IndexClauseSet *clauses, + List **bitindexpaths) +{ + List *indexpaths; + ListCell *lc; /* - * Find all the index paths that are directly usable for this relation - * (ie, are valid without considering OR or JOIN clauses). + * Build simple index paths using the clauses. Allow ScalarArrayOpExpr + * clauses only if the index AM supports them natively. */ - indexpaths = find_usable_indexes(root, rel, - rel->baserestrictinfo, NIL, - true, NULL, SAOP_PER_AM, ST_ANYSCAN); + indexpaths = build_index_paths(root, rel, + index, clauses, + index->predOK, + SAOP_PER_AM, ST_ANYSCAN); /* * Submit all the ones that can form plain IndexScan plans to add_path. - * (A plain IndexPath might represent either a plain IndexScan or an - * IndexOnlyScan, but for our purposes here the distinction does not + * (A plain IndexPath can represent either a plain IndexScan or an + * IndexOnlyScan, but for our purposes here that distinction does not * matter. However, some of the indexes might support only bitmap scans, - * and those we mustn't submit to add_path here.) Also, pick out the ones - * that might be useful as bitmap scans. For that, we must discard - * indexes that don't support bitmap scans, and we also are only - * interested in paths that have some selectivity; we should discard - * anything that was generated solely for ordering purposes. + * and those we mustn't submit to add_path here.) + * + * Also, pick out the ones that are usable as bitmap scans. For that, + * we must discard indexes that don't support bitmap scans, and we + * also are only interested in paths that have some selectivity; we + * should discard anything that was generated solely for ordering + * purposes. */ - bitindexpaths = NIL; - foreach(l, indexpaths) + foreach(lc, indexpaths) { - IndexPath *ipath = (IndexPath *) lfirst(l); + IndexPath *ipath = (IndexPath *) lfirst(lc); - if (ipath->indexinfo->amhasgettuple) + if (index->amhasgettuple) add_path(rel, (Path *) ipath); - if (ipath->indexinfo->amhasgetbitmap && + if (index->amhasgetbitmap && (ipath->path.pathkeys == NIL || ipath->indexselectivity < 1.0)) - bitindexpaths = lappend(bitindexpaths, ipath); + *bitindexpaths = lappend(*bitindexpaths, ipath); } /* - * Generate BitmapOrPaths for any suitable OR-clauses present in the - * restriction list. Add these to bitindexpaths. + * If the index doesn't handle ScalarArrayOpExpr clauses natively, + * check to see if there are any such clauses, and if so generate + * bitmap scan paths relying on executor-managed ScalarArrayOpExpr. */ - indexpaths = generate_bitmap_or_paths(root, rel, - rel->baserestrictinfo, NIL, - NULL); - bitindexpaths = list_concat(bitindexpaths, indexpaths); + if (!index->amsearcharray) + { + indexpaths = build_index_paths(root, rel, + index, clauses, + false, + SAOP_REQUIRE, ST_BITMAPSCAN); + *bitindexpaths = list_concat(*bitindexpaths, indexpaths); + } +} + +/* + * build_index_paths + * Given an index and a set of index clauses for it, construct zero + * or more IndexPaths. + * + * We return a list of paths because (1) this routine checks some cases + * that should cause us to not generate any IndexPath, and (2) in some + * cases we want to consider both a forward and a backward scan, so as + * to obtain both sort orders. Note that the paths are just returned + * to the caller and not immediately fed to add_path(). + * + * At top level, useful_predicate should be exactly the index's predOK flag + * (ie, true if it has a predicate that was proven from the restriction + * clauses). When working on an arm of an OR clause, useful_predicate + * should be true if the predicate required the current OR list to be proven. + * Note that this routine should never be called at all if the index has an + * unprovable predicate. + * + * saop_control indicates whether ScalarArrayOpExpr clauses can be used. + * When it's SAOP_REQUIRE, index paths are created only if we found at least + * one ScalarArrayOpExpr clause. + * + * scantype indicates whether we want to create plain indexscans, bitmap + * indexscans, or both. When it's ST_BITMAPSCAN, we will not consider + * index ordering while deciding if a Path is worth generating. + * + * 'rel' is the index's heap relation + * 'index' is the index for which we want to generate paths + * 'clauses' is the collection of indexable clauses (RestrictInfo nodes) + * 'useful_predicate' indicates whether the index has a useful predicate + * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used + * 'scantype' indicates whether we need plain or bitmap scan support + */ +static List * +build_index_paths(PlannerInfo *root, RelOptInfo *rel, + IndexOptInfo *index, IndexClauseSet *clauses, + bool useful_predicate, + SaOpControl saop_control, ScanTypeControl scantype) +{ + List *result = NIL; + IndexPath *ipath; + List *index_clauses; + List *clause_columns; + Relids outer_relids; + double loop_count; + List *orderbyclauses; + List *orderbyclausecols; + List *index_pathkeys; + List *useful_pathkeys; + bool found_clause; + bool pathkeys_possibly_useful; + bool index_is_ordered; + bool index_only_scan; + int indexcol; /* - * Likewise, generate paths using executor-managed ScalarArrayOpExpr - * clauses; these can't be simple indexscans but they can be used in - * bitmap scans. + * Check that index supports the desired scan type(s) */ - indexpaths = find_saop_paths(root, rel, - rel->baserestrictinfo, NIL, - true, NULL); - bitindexpaths = list_concat(bitindexpaths, indexpaths); + switch (scantype) + { + case ST_INDEXSCAN: + if (!index->amhasgettuple) + return NIL; + break; + case ST_BITMAPSCAN: + if (!index->amhasgetbitmap) + return NIL; + break; + case ST_ANYSCAN: + /* either or both are OK */ + break; + } /* - * If we found anything usable, generate a BitmapHeapPath for the most - * promising combination of bitmap index paths. + * 1. Collect the index clauses into a single list. + * + * We build a list of RestrictInfo nodes for clauses to be used with + * this index, along with an integer list of the index column numbers + * (zero based) that each clause should be used with. The clauses are + * ordered by index key, so that the column numbers form a nondecreasing + * sequence. (This order is depended on by btree and possibly other + * places.) The lists can be empty, if the index AM allows that. + * + * found_clause is set true only if there's at least one index clause; + * and if saop_control is SAOP_REQUIRE, it has to be a ScalarArrayOpExpr + * clause. + * + * We also build a Relids set showing which outer rels are required + * by the selected clauses. */ - if (bitindexpaths != NIL) + index_clauses = NIL; + clause_columns = NIL; + found_clause = false; + outer_relids = NULL; + for (indexcol = 0; indexcol < index->ncolumns; indexcol++) { - Path *bitmapqual; - BitmapHeapPath *bpath; + ListCell *lc; - bitmapqual = choose_bitmap_and(root, rel, bitindexpaths, NULL); - bpath = create_bitmap_heap_path(root, rel, bitmapqual, NULL); - add_path(rel, (Path *) bpath); + foreach(lc, clauses->indexclauses[indexcol]) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + if (IsA(rinfo->clause, ScalarArrayOpExpr)) + { + /* Ignore if not supported by index */ + if (saop_control == SAOP_PER_AM && !index->amsearcharray) + continue; + found_clause = true; + } + else + { + if (saop_control != SAOP_REQUIRE) + found_clause = true; + } + index_clauses = lappend(index_clauses, rinfo); + clause_columns = lappend_int(clause_columns, indexcol); + outer_relids = bms_add_members(outer_relids, + rinfo->clause_relids); + } + + /* + * If no clauses match the first index column, check for amoptionalkey + * restriction. We can't generate a scan over an index with + * amoptionalkey = false unless there's at least one index clause. + * (When working on columns after the first, this test cannot fail. + * It is always okay for columns after the first to not have any + * clauses.) + */ + if (index_clauses == NIL && !index->amoptionalkey) + return NIL; } -} + /* We do not want the index's rel itself listed in outer_relids */ + outer_relids = bms_del_member(outer_relids, rel->relid); + /* Enforce convention that outer_relids is exactly NULL if empty */ + if (bms_is_empty(outer_relids)) + outer_relids = NULL; + + /* Compute loop_count for cost estimation purposes */ + loop_count = get_loop_count(root, outer_relids); -/*---------- - * 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. + /* + * 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 + * if we are only trying to build bitmap indexscans. + */ + pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN && + has_useful_pathkeys(root, rel)); + index_is_ordered = (index->sortopfamily != NULL); + if (index_is_ordered && pathkeys_possibly_useful) + { + index_pathkeys = build_index_pathkeys(root, index, + ForwardScanDirection); + useful_pathkeys = truncate_useless_pathkeys(root, rel, + index_pathkeys); + orderbyclauses = NIL; + orderbyclausecols = NIL; + } + else if (index->amcanorderbyop && pathkeys_possibly_useful) + { + /* see if we can generate ordering operators for query_pathkeys */ + match_pathkeys_to_index(index, root->query_pathkeys, + &orderbyclauses, + &orderbyclausecols); + if (orderbyclauses) + useful_pathkeys = root->query_pathkeys; + else + useful_pathkeys = NIL; + } + else + { + useful_pathkeys = NIL; + orderbyclauses = NIL; + orderbyclausecols = NIL; + } + + /* + * 3. Check if an index-only scan is possible. If we're not building + * plain indexscans, this isn't relevant since bitmap scans don't support + * index data retrieval anyway. + */ + index_only_scan = (scantype != ST_BITMAPSCAN && + check_index_only(rel, index)); + + /* + * 4. 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 useful + * predicate, OR an index-only scan is possible. + */ + if (found_clause || useful_pathkeys != NIL || useful_predicate || + index_only_scan) + { + ipath = create_index_path(root, index, + index_clauses, + clause_columns, + orderbyclauses, + orderbyclausecols, + useful_pathkeys, + index_is_ordered ? + ForwardScanDirection : + NoMovementScanDirection, + index_only_scan, + outer_relids, + loop_count); + result = lappend(result, ipath); + } + + /* + * 5. If the index is ordered, a backwards scan might be interesting. + */ + if (index_is_ordered && pathkeys_possibly_useful) + { + index_pathkeys = build_index_pathkeys(root, index, + BackwardScanDirection); + useful_pathkeys = truncate_useless_pathkeys(root, rel, + index_pathkeys); + if (useful_pathkeys != NIL) + { + ipath = create_index_path(root, index, + index_clauses, + clause_columns, + NIL, + NIL, + useful_pathkeys, + BackwardScanDirection, + index_only_scan, + outer_relids, + loop_count); + result = lappend(result, ipath); + } + } + + return result; +} + +/* + * build_paths_for_OR + * Given a list of restriction clauses from one arm of an OR clause, + * construct all matching IndexPaths for the relation. + * + * Here we must scan all indexes of the relation, since a bitmap OR tree + * can use multiple indexes. * * The caller actually supplies two lists of restriction clauses: some - * "current" ones and some "outer" ones. Both lists can be used freely + * "current" ones and some "other" 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 @@ -281,85 +836,34 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) * 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) - * 'outer_rel' is the outer side of the join if forming an inner indexscan - * (so some of the given clauses are join clauses); NULL if not - * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used - * 'scantype' indicates whether we need plain or bitmap scan support - * - * Note: check_partial_indexes() must have been run previously. - *---------- + * 'other_clauses' is the list of additional upper-level clauses */ static List * -find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, - List *clauses, List *outer_clauses, - bool istoplevel, RelOptInfo *outer_rel, - SaOpControl saop_control, ScanTypeControl scantype) +build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel, + List *clauses, List *other_clauses) { - Relids outer_relids = outer_rel ? outer_rel->relids : NULL; - bool possibly_useful_pathkeys = has_useful_pathkeys(root, rel); List *result = NIL; List *all_clauses = NIL; /* not computed till needed */ - ListCell *ilist; + ListCell *lc; - foreach(ilist, rel->indexlist) + foreach(lc, rel->indexlist) { - IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); - IndexPath *ipath; - List *restrictclauses; - List *restrictclausecols; - List *orderbyclauses; - List *orderbyclausecols; - List *index_pathkeys; - List *useful_pathkeys; + IndexOptInfo *index = (IndexOptInfo *) lfirst(lc); + IndexClauseSet clauseset; + List *indexpaths; bool useful_predicate; - bool found_clause; - bool index_is_ordered; - bool index_only_scan; - /* - * Check that index supports the desired scan type(s) - */ - switch (scantype) - { - case ST_INDEXSCAN: - if (!index->amhasgettuple) - continue; - break; - case ST_BITMAPSCAN: - if (!index->amhasgetbitmap) - continue; - break; - case ST_ANYSCAN: - /* either or both are OK */ - break; - } - - /* - * If we're doing find_saop_paths(), we can skip indexes that support - * ScalarArrayOpExpr natively. We already generated all the potential - * indexpaths for them, so no need to do anything more. - */ - if (saop_control == SAOP_REQUIRE && index->amsearcharray) + /* Ignore index if it doesn't support bitmap scans */ + if (!index->amhasgetbitmap) continue; /* * 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. + * index is marked predOK then we know it's OK. Otherwise, we have + * to test whether the added clauses are sufficient to imply the + * predicate. If so, we can 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 @@ -372,218 +876,87 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, { if (index->predOK) { - if (istoplevel) - { - /* we know predicate was proven from these clauses */ - useful_predicate = true; - } + /* Usable, but don't set useful_predicate */ } 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); + other_clauses); if (!predicate_implied_by(index->indpred, all_clauses)) continue; /* can't use it at all */ - if (!predicate_implied_by(index->indpred, outer_clauses)) + if (!predicate_implied_by(index->indpred, other_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 (and, if saop_control is SAOP_REQUIRE, it has to - * have been a ScalarArrayOpExpr clause). + * Identify the restriction clauses that can match the index. */ - match_clauses_to_index(index, - clauses, - outer_clauses, - outer_relids, - saop_control, - &restrictclauses, - &restrictclausecols, - &found_clause); + MemSet(&clauseset, 0, sizeof(clauseset)); + match_clauses_to_index(index, clauses, &clauseset); /* - * 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 no matches so far, and the index predicate isn't useful, + * we don't want it. */ - if (restrictclauses == NIL && !index->amoptionalkey) + if (!clauseset.nonempty && !useful_predicate) 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 = (index->sortopfamily != NULL); - if (index_is_ordered && possibly_useful_pathkeys && - istoplevel && outer_rel == NULL) - { - index_pathkeys = build_index_pathkeys(root, index, - ForwardScanDirection); - useful_pathkeys = truncate_useless_pathkeys(root, rel, - index_pathkeys); - orderbyclauses = NIL; - orderbyclausecols = NIL; - } - else if (index->amcanorderbyop && possibly_useful_pathkeys && - istoplevel && outer_rel == NULL && scantype != ST_BITMAPSCAN) - { - /* see if we can generate ordering operators for query_pathkeys */ - match_pathkeys_to_index(index, root->query_pathkeys, - &orderbyclauses, - &orderbyclausecols); - if (orderbyclauses) - useful_pathkeys = root->query_pathkeys; - else - useful_pathkeys = NIL; - } - else - { - useful_pathkeys = NIL; - orderbyclauses = NIL; - orderbyclausecols = NIL; - } - - /* - * 3. Check if an index-only scan is possible. - */ - index_only_scan = check_index_only(rel, index); - - /* - * 4. 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, - * OR an index-only scan is possible. + * Add "other" restriction clauses to the clauseset. */ - if (found_clause || useful_pathkeys != NIL || useful_predicate || - index_only_scan) - { - ipath = create_index_path(root, index, - restrictclauses, - restrictclausecols, - orderbyclauses, - orderbyclausecols, - useful_pathkeys, - index_is_ordered ? - ForwardScanDirection : - NoMovementScanDirection, - index_only_scan, - outer_rel); - result = lappend(result, ipath); - } + match_clauses_to_index(index, other_clauses, &clauseset); /* - * 5. If the index is ordered, a backwards scan might be interesting. - * Again, this is only interesting at top level. + * Construct paths if possible. */ - if (index_is_ordered && possibly_useful_pathkeys && - istoplevel && outer_rel == NULL) - { - index_pathkeys = build_index_pathkeys(root, index, - BackwardScanDirection); - useful_pathkeys = truncate_useless_pathkeys(root, rel, - index_pathkeys); - if (useful_pathkeys != NIL) - { - ipath = create_index_path(root, index, - restrictclauses, - restrictclausecols, - NIL, - NIL, - useful_pathkeys, - BackwardScanDirection, - index_only_scan, - outer_rel); - result = lappend(result, ipath); - } - } + indexpaths = build_index_paths(root, rel, + index, &clauseset, + useful_predicate, + SAOP_ALLOW, ST_BITMAPSCAN); + result = list_concat(result, indexpaths); } return result; } - -/* - * find_saop_paths - * Find all the potential indexpaths that make use of executor-managed - * ScalarArrayOpExpr clauses. The executor only supports these in bitmap - * scans, not plain indexscans, so we need to segregate them from the - * normal case. Otherwise, same API as find_usable_indexes(). - * Returns a list of IndexPaths. - */ -static List * -find_saop_paths(PlannerInfo *root, RelOptInfo *rel, - List *clauses, List *outer_clauses, - bool istoplevel, RelOptInfo *outer_rel) -{ - bool have_saop = false; - ListCell *l; - - /* - * Since find_usable_indexes is relatively expensive, don't bother to run - * it unless there are some top-level ScalarArrayOpExpr clauses. - */ - foreach(l, clauses) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - - Assert(IsA(rinfo, RestrictInfo)); - if (IsA(rinfo->clause, ScalarArrayOpExpr)) - { - have_saop = true; - break; - } - } - if (!have_saop) - return NIL; - - return find_usable_indexes(root, rel, - clauses, outer_clauses, - istoplevel, outer_rel, - SAOP_REQUIRE, ST_BITMAPSCAN); -} - - /* * 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 + * other_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.) outer_rel is the outer - * side when we are considering a nestloop inner indexpath. + * ORs. (See build_paths_for_OR() for motivation.) + * + * If restriction_only is true, ignore OR elements that are join clauses. + * When using this feature it is caller's responsibility that neither clauses + * nor other_clauses contain any join clauses that are not ORs, as we do not + * re-filter those lists. */ List * generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, - List *clauses, List *outer_clauses, - RelOptInfo *outer_rel) + List *clauses, List *other_clauses, + bool restriction_only) { List *result = NIL; List *all_clauses; - ListCell *l; + ListCell *lc; /* - * We can use both the current and outer clauses as context for - * find_usable_indexes + * We can use both the current and other clauses as context for + * build_paths_for_OR; no need to remove ORs from the lists. */ - all_clauses = list_concat(list_copy(clauses), outer_clauses); + all_clauses = list_concat(list_copy(clauses), other_clauses); - foreach(l, clauses) + foreach(lc, clauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); List *pathlist; Path *bitmapqual; ListCell *j; @@ -608,31 +981,34 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, { List *andargs = ((BoolExpr *) orarg)->args; - indlist = find_usable_indexes(root, rel, - andargs, - all_clauses, - false, - outer_rel, - SAOP_ALLOW, - ST_BITMAPSCAN); + if (restriction_only) + andargs = drop_indexable_join_clauses(rel, andargs); + + indlist = build_paths_for_OR(root, rel, + andargs, + all_clauses); + /* Recurse in case there are sub-ORs */ indlist = list_concat(indlist, generate_bitmap_or_paths(root, rel, andargs, all_clauses, - outer_rel)); + restriction_only)); } else { + List *orargs; + Assert(IsA(orarg, RestrictInfo)); Assert(!restriction_is_or_clause((RestrictInfo *) orarg)); - indlist = find_usable_indexes(root, rel, - list_make1(orarg), - all_clauses, - false, - outer_rel, - SAOP_ALLOW, - ST_BITMAPSCAN); + orargs = list_make1(orarg); + + if (restriction_only) + orargs = drop_indexable_join_clauses(rel, orargs); + + indlist = build_paths_for_OR(root, rel, + orargs, + all_clauses); } /* @@ -649,7 +1025,7 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, * OK, pick the most promising AND combination, and add it to * pathlist. */ - bitmapqual = choose_bitmap_and(root, rel, indlist, outer_rel); + bitmapqual = choose_bitmap_and(root, rel, indlist); pathlist = lappend(pathlist, bitmapqual); } @@ -667,6 +1043,34 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, return result; } +/* + * drop_indexable_join_clauses + * Remove any indexable join clauses from the list. + * + * This is a helper for generate_bitmap_or_paths(). We leave OR clauses + * in the list whether they are joins or not, since we might be able to + * extract a restriction item from an OR list. It's safe to leave such + * clauses in the list because match_clauses_to_index() will ignore them, + * so there's no harm in passing such clauses to build_paths_for_OR(). + */ +static List * +drop_indexable_join_clauses(RelOptInfo *rel, List *clauses) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, clauses) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + Assert(IsA(rinfo, RestrictInfo)); + if (restriction_is_or_clause(rinfo) || + bms_is_subset(rinfo->clause_relids, rel->relids)) + result = lappend(result, rinfo); + } + return result; +} + /* * choose_bitmap_and @@ -680,8 +1084,7 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel, * combining multiple inputs. */ static Path * -choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, - List *paths, RelOptInfo *outer_rel) +choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths) { int npaths = list_length(paths); PathClauseUsage **pathinfoarray; @@ -729,7 +1132,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, * reduces the total cost. Perhaps someday that code will be smarter and * we can remove this limitation. (But note that this also defends * against flat-out duplicate input paths, which can happen because - * best_inner_indexscan will find the same OR join clauses that + * match_join_clauses_to_index will find the same OR join clauses that * create_or_index_quals has pulled OR restriction clauses out of.) * * For the same reason, we reject AND combinations in which an index @@ -807,7 +1210,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, pathinfo = pathinfoarray[i]; paths = list_make1(pathinfo->path); - costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path, outer_rel); + costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path); qualsofar = list_concat(list_copy(pathinfo->quals), list_copy(pathinfo->preds)); clauseidsofar = bms_copy(pathinfo->clauseids); @@ -841,7 +1244,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, } /* tentatively add new path to paths, so we can estimate cost */ paths = lappend(paths, pathinfo->path); - newcost = bitmap_and_cost_est(root, rel, paths, outer_rel); + newcost = bitmap_and_cost_est(root, rel, paths); if (newcost < costsofar) { /* keep new path in paths, update subsidiary variables */ @@ -913,14 +1316,23 @@ path_usage_comparator(const void *a, const void *b) * index path (no BitmapAnd, at least not at this level). */ static Cost -bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, - Path *ipath, RelOptInfo *outer_rel) +bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath) { - Path bpath; + BitmapHeapPath bpath; + + /* Set up a dummy BitmapHeapPath */ + bpath.path.type = T_BitmapHeapPath; + bpath.path.pathtype = T_BitmapHeapScan; + bpath.path.parent = rel; + bpath.path.pathkeys = NIL; + bpath.path.required_outer = ipath->required_outer; + bpath.path.param_clauses = ipath->param_clauses; + bpath.bitmapqual = ipath; - cost_bitmap_heap_scan(&bpath, root, rel, ipath, outer_rel); + cost_bitmap_heap_scan((Path *) &bpath, root, rel, ipath, + get_loop_count(root, bpath.path.required_outer)); - return bpath.total_cost; + return bpath.path.total_cost; } /* @@ -928,22 +1340,32 @@ bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, * inputs. */ static Cost -bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, - List *paths, RelOptInfo *outer_rel) +bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths) { - BitmapAndPath apath; - Path bpath; + BitmapAndPath *apath; + BitmapHeapPath bpath; - /* Set up a dummy BitmapAndPath */ - apath.path.type = T_BitmapAndPath; - apath.path.parent = rel; - apath.bitmapquals = paths; - cost_bitmap_and_node(&apath, root); + /* + * Create a temporary BitmapAndPath. (Because it needs realistic + * required_outer and param_clauses values, making a dummy one would + * take more code than it's worth.) + */ + apath = create_bitmap_and_path(root, rel, paths); + + /* Set up a dummy BitmapHeapPath */ + bpath.path.type = T_BitmapHeapPath; + bpath.path.pathtype = T_BitmapHeapScan; + bpath.path.parent = rel; + bpath.path.pathkeys = NIL; + bpath.path.required_outer = apath->path.required_outer; + bpath.path.param_clauses = apath->path.param_clauses; + bpath.bitmapqual = (Path *) apath; /* Now we can do cost_bitmap_heap_scan */ - cost_bitmap_heap_scan(&bpath, root, rel, (Path *) &apath, outer_rel); + cost_bitmap_heap_scan((Path *) &bpath, root, rel, (Path *) apath, + get_loop_count(root, bpath.path.required_outer)); - return bpath.total_cost; + return bpath.path.total_cost; } @@ -1150,128 +1572,253 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) return result; } +/* + * get_loop_count + * Choose the loop count estimate to use for costing a parameterized path + * with the given set of outer relids. + * + * Since we produce parameterized paths before we've begun to generate join + * relations, it's impossible to predict exactly how many times a parameterized + * path will be iterated; we don't know the size of the relation that will be + * on the outside of the nestloop. However, we should try to account for + * multiple iterations somehow in costing the path. The heuristic embodied + * here is to use the rowcount of the smallest other base relation needed in + * the join clauses used by the path. (We could alternatively consider the + * largest one, but that seems too optimistic.) This is of course the right + * answer for single-other-relation cases, and it seems like a reasonable + * zero-order approximation for multiway-join cases. + * + * Note: for this to work, allpaths.c must establish all baserel size + * estimates before it begins to compute paths, or at least before it + * calls create_index_paths(). + */ +static double +get_loop_count(PlannerInfo *root, Relids outer_relids) +{ + double result = 1.0; + + /* For a non-parameterized path, just return 1.0 quickly */ + if (outer_relids != NULL) + { + int relid; + + /* Need a working copy since bms_first_member is destructive */ + outer_relids = bms_copy(outer_relids); + while ((relid = bms_first_member(outer_relids)) >= 0) + { + RelOptInfo *outer_rel; + + /* Paranoia: ignore bogus relid indexes */ + if (relid >= root->simple_rel_array_size) + continue; + outer_rel = root->simple_rel_array[relid]; + if (outer_rel == NULL) + continue; + Assert(outer_rel->relid == relid); /* sanity check on array */ + + /* Other relation could be proven empty, if so ignore */ + if (IS_DUMMY_REL(outer_rel)) + continue; + + /* Otherwise, rel's rows estimate should be valid by now */ + Assert(outer_rel->rows > 0); + + /* Remember smallest row count estimate among the outer rels */ + if (result == 1.0 || result > outer_rel->rows) + result = outer_rel->rows; + } + bms_free(outer_relids); + } + return result; +} + /**************************************************************************** - * ---- ROUTINES TO CHECK RESTRICTIONS ---- + * ---- ROUTINES TO CHECK QUERY CLAUSES ---- ****************************************************************************/ +/* + * match_restriction_clauses_to_index + * Identify restriction clauses for the rel that match the index. + * Matching clauses are added to *clauseset. + */ +static void +match_restriction_clauses_to_index(RelOptInfo *rel, IndexOptInfo *index, + IndexClauseSet *clauseset) +{ + match_clauses_to_index(index, rel->baserestrictinfo, clauseset); +} /* - * match_clauses_to_index - * Find restriction clauses that can be used with an index. - * - * Returns a list of RestrictInfo nodes for clauses that can be used with - * this index, along with an integer list of the index column numbers - * (zero based) that each clause would be used with. The clauses are - * ordered by index key, so that the column numbers form a nondecreasing - * sequence. (This order is depended on by btree and possibly other places.) - * NIL lists are returned if there are no matching clauses. - * - * We can use clauses from either the current clauses or outer_clauses lists, - * but *found_clause is set TRUE only if we used at least one clause from - * the "current clauses" list. See find_usable_indexes() for motivation. - * - * outer_relids determines what Vars will be allowed on the other side - * of a possible index qual; see match_clause_to_indexcol(). - * - * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used. - * When it's SAOP_REQUIRE, *found_clause is set TRUE only if we used at least - * one ScalarArrayOpExpr from the current clauses list. - * - * If the index has amoptionalkey = false, we give up and return NIL when - * there are no restriction clauses matching the first index key. Otherwise, - * we return NIL only if there are no restriction clauses matching any index - * key. There could be unused index keys after the first one in any case. - * - * Note: in some circumstances we may find the same RestrictInfos coming - * from multiple places. Defend against redundant outputs by refusing to - * match an already-used clause (pointer equality should be a good enough - * check for this). This also keeps us from matching the same clause to - * multiple columns of a badly-defined index, which is unlikely to be helpful - * and is likely to give us an inflated idea of the index's selectivity. + * match_join_clauses_to_index + * Identify join clauses for the rel that match the index. + * Matching clauses are added to *clauseset. + * Also, add any potentially usable join OR clauses to *joinorclauses. */ static void -match_clauses_to_index(IndexOptInfo *index, - List *clauses, List *outer_clauses, - Relids outer_relids, - SaOpControl saop_control, - List **index_clauses_p, - List **clause_columns_p, - bool *found_clause) +match_join_clauses_to_index(PlannerInfo *root, + RelOptInfo *rel, IndexOptInfo *index, + IndexClauseSet *clauseset, + List **joinorclauses) { - List *index_clauses = NIL; - List *clause_columns = NIL; - int indexcol; + Relids inner_baserels; + ListCell *lc; - *index_clauses_p = NIL; /* set default results */ - *clause_columns_p = NIL; - *found_clause = false; + /* + * There is no value in considering join clauses for outer joins in which + * the indexed relation is on the outside, since there'd be no way to + * perform such a join with a parameterized nestloop. So first, identify + * all baserels that are on the inside of such joins. + */ + inner_baserels = NULL; + foreach(lc, root->join_info_list) + { + SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); - if (clauses == NIL && outer_clauses == NIL) - return; /* cannot succeed */ + if (bms_overlap(rel->relids, sjinfo->min_lefthand)) + inner_baserels = bms_add_members(inner_baserels, + sjinfo->min_righthand); + /* full joins constrain both sides symmetrically */ + if (sjinfo->jointype == JOIN_FULL && + bms_overlap(rel->relids, sjinfo->min_righthand)) + inner_baserels = bms_add_members(inner_baserels, + sjinfo->min_lefthand); + } - for (indexcol = 0; indexcol < index->ncolumns; indexcol++) + /* Now scan the rel's join clauses */ + foreach(lc, rel->joininfo) { - ListCell *l; + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - /* check the current clauses */ - foreach(l, clauses) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + /* Ignore if it mentions anything from wrong side of an outer join */ + if (bms_overlap(rinfo->clause_relids, inner_baserels)) + continue; - Assert(IsA(rinfo, RestrictInfo)); - if (list_member_ptr(index_clauses, rinfo)) - continue; - if (match_clause_to_indexcol(index, - indexcol, - rinfo, - outer_relids, - saop_control)) - { - index_clauses = lappend(index_clauses, rinfo); - clause_columns = lappend_int(clause_columns, indexcol); - if (saop_control != SAOP_REQUIRE || - IsA(rinfo->clause, ScalarArrayOpExpr)) - *found_clause = true; - } - } + /* + * Note that we ignore required_relids; that's okay because we are + * intentionally ignoring the normal rules for placing evaluation of + * join clauses. The whole point here is to evaluate join clauses + * below their join, even if they would normally be delayed by + * outer join rules. + * + * Instead of considering required_relids, we ignore clauses for which + * any referenced rel is in nullable_relids; that means there's an + * outer join below the clause and so it can't be checked at the + * relation scan level. + * + * Note: unlike create_or_index_quals(), we can accept clauses that + * are marked !is_pushed_down (ie they are themselves outer-join + * clauses). This is OK because any path generated with these clauses + * could only be used in the inside of a nestloop join, which will be + * the nullable side. + */ + if (bms_overlap(rinfo->clause_relids, rinfo->nullable_relids)) + continue; - /* check the outer clauses */ - foreach(l, outer_clauses) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + /* Potentially usable, so see if it matches the index or is an OR */ + if (restriction_is_or_clause(rinfo)) + *joinorclauses = lappend(*joinorclauses, rinfo); + else + match_clause_to_index(index, rinfo, clauseset); + } +} - Assert(IsA(rinfo, RestrictInfo)); - if (list_member_ptr(index_clauses, rinfo)) - continue; - if (match_clause_to_indexcol(index, - indexcol, - rinfo, - outer_relids, - saop_control)) - { - index_clauses = lappend(index_clauses, rinfo); - clause_columns = lappend_int(clause_columns, indexcol); - } - } +/* + * match_eclass_clauses_to_index + * Identify EquivalenceClass join clauses for the rel that match the index. + * Matching clauses are added to *clauseset. + */ +static void +match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index, + IndexClauseSet *clauseset) +{ + int indexcol; + + /* No work if rel is not in any such ECs */ + if (!index->rel->has_eclass_joins) + return; + + for (indexcol = 0; indexcol < index->ncolumns; indexcol++) + { + List *clauses; + + clauses = generate_implied_equalities_for_indexcol(root, + index, + indexcol); /* - * If no clauses match this key, check for amoptionalkey restriction. + * We have to check whether the results actually do match the index, + * since for non-btree indexes the EC's equality operators might not + * be in the index opclass (cf eclass_member_matches_indexcol). */ - if (index_clauses == NIL && !index->amoptionalkey) - return; + match_clauses_to_index(index, clauses, clauseset); } +} - *index_clauses_p = index_clauses; - *clause_columns_p = clause_columns; +/* + * match_clauses_to_index + * Perform match_clause_to_index() for each clause in a list. + * Matching clauses are added to *clauseset. + */ +static void +match_clauses_to_index(IndexOptInfo *index, + List *clauses, + IndexClauseSet *clauseset) +{ + ListCell *lc; + + foreach(lc, clauses) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + Assert(IsA(rinfo, RestrictInfo)); + match_clause_to_index(index, rinfo, clauseset); + } } +/* + * match_clause_to_index + * Test whether a qual clause can be used with an index. + * + * If the clause is usable, add it to the appropriate list in *clauseset. + * *clauseset must be initialized to zeroes before first call. + * + * Note: in some circumstances we may find the same RestrictInfos coming from + * multiple places. Defend against redundant outputs by refusing to add a + * clause twice (pointer equality should be a good enough check for this). + * + * Note: it's possible that a badly-defined index could have multiple matching + * columns. We always select the first match if so; this avoids scenarios + * wherein we get an inflated idea of the index's selectivity by using the + * same clause multiple times with different index columns. + */ +static void +match_clause_to_index(IndexOptInfo *index, + RestrictInfo *rinfo, + IndexClauseSet *clauseset) +{ + int indexcol; + + for (indexcol = 0; indexcol < index->ncolumns; indexcol++) + { + if (match_clause_to_indexcol(index, + indexcol, + rinfo)) + { + clauseset->indexclauses[indexcol] = + list_append_unique_ptr(clauseset->indexclauses[indexcol], + rinfo); + clauseset->nonempty = true; + return; + } + } +} /* * match_clause_to_indexcol() * Determines whether a restriction clause matches a column of an index. * - * To match a normal index, the clause: + * To match an index normally, the clause: * * (1) must be in the form (indexkey op const) or (const op indexkey); * and @@ -1281,18 +1828,17 @@ match_clauses_to_index(IndexOptInfo *index, * and * (3) must match the collation of the index, if collation is relevant. * - * 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. + * Our definition of "const" is exceedingly liberal: we allow anything that + * doesn't involve a volatile function or a Var of the index's relation. + * In particular, Vars belonging to other relations of the query are + * accepted here, since a clause of that form can be used in a + * parameterized indexscan. It's the responsibility of higher code levels + * to manage restriction and join clauses appropriately. * - * 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. + * Note: we do need to check for Vars of the index's relation on the + * "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3)) + * are not processable by a parameterized indexscan on a.f1, whereas + * something like (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 @@ -1316,10 +1862,7 @@ match_clauses_to_index(IndexOptInfo *index, * adjust_rowcompare_for_index(). * * It is also possible to match ScalarArrayOpExpr clauses to indexes, when - * the clause is of the form "indexkey op ANY (arrayconst)". Since not - * all indexes handle these natively, and the executor implements them - * only in the context of bitmap index scans, our caller specifies whether - * to allow these or not. + * the clause is of the form "indexkey op ANY (arrayconst)". * * For boolean indexes, it is also possible to match the clause directly * to the indexkey; or perhaps the clause is (NOT indexkey). @@ -1327,8 +1870,6 @@ match_clauses_to_index(IndexOptInfo *index, * 'index' is the index of interest. * 'indexcol' is a column number of 'index' (counting from 0). * 'rinfo' is the clause to be tested (as a RestrictInfo node). - * 'outer_relids' lists rels whose Vars can be considered pseudoconstant. - * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used. * * Returns true if the clause can be used with this index key. * @@ -1338,11 +1879,10 @@ match_clauses_to_index(IndexOptInfo *index, static bool match_clause_to_indexcol(IndexOptInfo *index, int indexcol, - RestrictInfo *rinfo, - Relids outer_relids, - SaOpControl saop_control) + RestrictInfo *rinfo) { Expr *clause = rinfo->clause; + Index index_relid = index->rel->relid; Oid opfamily = index->opfamily[indexcol]; Oid idxcollation = index->indexcollations[indexcol]; Node *leftop, @@ -1387,8 +1927,7 @@ match_clause_to_indexcol(IndexOptInfo *index, expr_coll = ((OpExpr *) clause)->inputcollid; plain_op = true; } - else if (clause && IsA(clause, ScalarArrayOpExpr) && - (index->amsearcharray || saop_control != SAOP_PER_AM)) + else if (clause && IsA(clause, ScalarArrayOpExpr)) { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; @@ -1407,8 +1946,7 @@ match_clause_to_indexcol(IndexOptInfo *index, { return match_rowcompare_to_indexcol(index, indexcol, opfamily, idxcollation, - (RowCompareExpr *) clause, - outer_relids); + (RowCompareExpr *) clause); } else if (index->amsearchnulls && IsA(clause, NullTest)) { @@ -1427,7 +1965,7 @@ match_clause_to_indexcol(IndexOptInfo *index, * (constant operator indexkey). See above notes about const-ness. */ if (match_index_to_operand(leftop, indexcol, index) && - bms_is_subset(right_relids, outer_relids) && + !bms_is_member(index_relid, right_relids) && !contain_volatile_functions(rightop)) { if (IndexCollMatchesExprColl(idxcollation, expr_coll) && @@ -1439,14 +1977,15 @@ match_clause_to_indexcol(IndexOptInfo *index, * is a "special" indexable operator. */ if (plain_op && - match_special_index_operator(clause, opfamily, idxcollation, true)) + match_special_index_operator(clause, opfamily, + idxcollation, true)) return true; return false; } if (plain_op && match_index_to_operand(rightop, indexcol, index) && - bms_is_subset(left_relids, outer_relids) && + !bms_is_member(index_relid, left_relids) && !contain_volatile_functions(leftop)) { if (IndexCollMatchesExprColl(idxcollation, expr_coll) && @@ -1457,7 +1996,8 @@ match_clause_to_indexcol(IndexOptInfo *index, * If we didn't find a member of the index's opfamily, see whether it * is a "special" indexable operator. */ - if (match_special_index_operator(clause, opfamily, idxcollation, false)) + if (match_special_index_operator(clause, opfamily, + idxcollation, false)) return true; return false; } @@ -1498,9 +2038,9 @@ match_rowcompare_to_indexcol(IndexOptInfo *index, int indexcol, Oid opfamily, Oid idxcollation, - RowCompareExpr *clause, - Relids outer_relids) + RowCompareExpr *clause) { + Index index_relid = index->rel->relid; Node *leftop, *rightop; Oid expr_op; @@ -1533,13 +2073,13 @@ match_rowcompare_to_indexcol(IndexOptInfo *index, * These syntactic tests are the same as in match_clause_to_indexcol() */ if (match_index_to_operand(leftop, indexcol, index) && - bms_is_subset(pull_varnos(rightop), outer_relids) && + !bms_is_member(index_relid, pull_varnos(rightop)) && !contain_volatile_functions(rightop)) { /* OK, indexkey is on left */ } else if (match_index_to_operand(rightop, indexcol, index) && - bms_is_subset(pull_varnos(leftop), outer_relids) && + !bms_is_member(index_relid, pull_varnos(leftop)) && !contain_volatile_functions(leftop)) { /* indexkey is on right, so commute the operator */ @@ -1800,484 +2340,41 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel) } /**************************************************************************** - * ---- ROUTINES TO CHECK JOIN CLAUSES ---- + * ---- ROUTINES TO CHECK EXTERNALLY-VISIBLE CONDITIONS ---- ****************************************************************************/ /* - * indexable_outerrelids - * Finds all other relids that participate in any indexable join clause - * for the specified table. Returns a set of relids. - */ -static Relids -indexable_outerrelids(PlannerInfo *root, RelOptInfo *rel) -{ - Relids outer_relids = NULL; - bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL); - ListCell *lc1; - - /* - * 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(lc1, rel->joininfo) - { - RestrictInfo *joininfo = (RestrictInfo *) lfirst(lc1); - 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); - } - - /* - * We also have to look through the query's EquivalenceClasses to see if - * any of them could generate indexable join conditions for this rel. - */ - if (rel->has_eclass_joins) - { - foreach(lc1, root->eq_classes) - { - EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); - Relids other_rels = NULL; - bool found_index = false; - ListCell *lc2; - - /* - * Won't generate joinclauses if const or single-member (the - * latter test covers the volatile case too) - */ - if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1) - continue; - - /* - * Note we don't test ec_broken; if we did, we'd need a separate - * code path to look through ec_sources. Checking the members - * anyway is OK as a possibly-overoptimistic heuristic. - */ - - /* - * No point in searching if rel not mentioned in eclass (but we - * can't tell that for a child rel). - */ - if (!is_child_rel && - !bms_is_subset(rel->relids, cur_ec->ec_relids)) - continue; - - /* - * Scan members, looking for both an index match and join - * candidates - */ - foreach(lc2, cur_ec->ec_members) - { - EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2); - - /* Join candidate? */ - if (!cur_em->em_is_child && - !bms_overlap(cur_em->em_relids, rel->relids)) - { - other_rels = bms_add_members(other_rels, - cur_em->em_relids); - continue; - } - - /* Check for index match (only need one) */ - if (!found_index && - bms_equal(cur_em->em_relids, rel->relids) && - eclass_matches_any_index(cur_ec, cur_em, rel)) - found_index = true; - } - - if (found_index) - 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 bool -matches_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; - - for (indexcol = 0; indexcol < index->ncolumns; indexcol++) - { - if (match_clause_to_indexcol(index, - indexcol, - rinfo, - outer_relids, - SAOP_ALLOW)) - return true; - } - } - - return false; -} - -/* - * eclass_matches_any_index - * Workhorse for indexable_outerrelids: see if an EquivalenceClass member - * can be matched to any index column of the given rel. + * eclass_member_matches_indexcol + * Test whether an EquivalenceClass member matches an index column. * - * This is also exported for use by find_eclass_clauses_for_index_join. + * This is exported for use by generate_implied_equalities_for_indexcol. */ bool -eclass_matches_any_index(EquivalenceClass *ec, EquivalenceMember *em, - RelOptInfo *rel) +eclass_member_matches_indexcol(EquivalenceClass *ec, EquivalenceMember *em, + IndexOptInfo *index, int indexcol) { - ListCell *l; - - foreach(l, rel->indexlist) - { - IndexOptInfo *index = (IndexOptInfo *) lfirst(l); - int indexcol; - - for (indexcol = 0; indexcol < index->ncolumns; indexcol++) - { - Oid curFamily = index->opfamily[indexcol]; - Oid curCollation = index->indexcollations[indexcol]; - - /* - * If it's a btree index, we can reject it if its opfamily isn't - * compatible with the EC, since no clause generated from the EC - * could be used with the index. For non-btree indexes, we can't - * easily tell whether clauses generated from the EC could be used - * with the index, so only check for expression match. This might - * mean we return "true" for a useless index, but that will just - * cause some wasted planner cycles; it's better than ignoring - * useful indexes. - * - * We insist on collation match for all index types, though. - */ - if ((index->relam != BTREE_AM_OID || - list_member_oid(ec->ec_opfamilies, curFamily)) && - IndexCollMatchesExprColl(curCollation, ec->ec_collation) && - match_index_to_operand((Node *) em->em_expr, indexcol, index)) - return true; - } - } - - return false; -} - - -/* - * best_inner_indexscan - * Finds the best available inner indexscans for a nestloop join - * with the given rel on the inside and the given outer_rel outside. - * - * *cheapest_startup gets the path with least startup cost - * *cheapest_total gets the path with least total cost (often the same path) - * Both are set to NULL if there are no possible inner indexscans. - * - * We ignore ordering considerations, since a nestloop's inner scan's order - * is uninteresting. Hence startup cost and total cost are the only figures - * of merit to consider. - * - * Note: create_index_paths() must have been run previously for this rel, - * else the results will always be NULL. - */ -void -best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel, - RelOptInfo *outer_rel, JoinType jointype, - Path **cheapest_startup, Path **cheapest_total) -{ - Relids outer_relids; - bool isouterjoin; - List *clause_list; - List *indexpaths; - List *bitindexpaths; - List *allindexpaths; - ListCell *l; - InnerIndexscanInfo *info; - MemoryContext oldcontext; - - /* Initialize results for failure returns */ - *cheapest_startup = *cheapest_total = NULL; - - /* - * Nestloop only supports inner, left, semi, and anti joins. - */ - switch (jointype) - { - case JOIN_INNER: - case JOIN_SEMI: - isouterjoin = false; - break; - case JOIN_LEFT: - case JOIN_ANTI: - isouterjoin = true; - break; - default: - return; - } - - /* - * If there are no indexable joinclauses for this rel, exit quickly. - */ - if (bms_is_empty(rel->index_outer_relids)) - return; + Oid curFamily = index->opfamily[indexcol]; + Oid curCollation = index->indexcollations[indexcol]; /* - * Otherwise, we have to do path selection in the main planning context, - * 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.) + * If it's a btree index, we can reject it if its opfamily isn't + * compatible with the EC, since no clause generated from the EC could be + * used with the index. For non-btree indexes, we can't easily tell + * whether clauses generated from the EC could be used with the index, + * so don't check the opfamily. This might mean we return "true" for a + * useless EC, so we have to recheck the results of + * generate_implied_equalities_for_indexcol; see + * match_eclass_clauses_to_index. */ - oldcontext = MemoryContextSwitchTo(root->planner_cxt); - - /* - * 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_rel->relids); - if (bms_is_empty(outer_relids)) - { - bms_free(outer_relids); - MemoryContextSwitchTo(oldcontext); - return; - } - - /* - * 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 combination of rels.) - * - * NOTE: because we cache on outer_relids rather than outer_rel->relids, - * we will report the same paths and hence path cost for joins with - * different sets of irrelevant rels on the outside. Now that cost_index - * is sensitive to outer_rel->rows, this is not really right. However the - * error is probably not large. Is it worth establishing a separate cache - * entry for each distinct outer_rel->relids set to get this right? - */ - 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); - *cheapest_startup = info->cheapest_startup_innerpath; - *cheapest_total = info->cheapest_total_innerpath; - return; - } - } - - /* - * 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. Also, even if the "best inner - * indexscan" is just a plain indexscan, it will have a different cost - * estimate because of cache effects. - */ - 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 and executor-managed ScalarArrayOpExpr clauses. - */ - allindexpaths = find_usable_indexes(root, rel, - clause_list, NIL, - false, outer_rel, - SAOP_PER_AM, - ST_ANYSCAN); - - /* - * Include the ones that are usable as plain indexscans in indexpaths, and - * include the ones that are usable as bitmap scans in bitindexpaths. - */ - indexpaths = bitindexpaths = NIL; - foreach(l, allindexpaths) - { - IndexPath *ipath = (IndexPath *) lfirst(l); - - if (ipath->indexinfo->amhasgettuple) - indexpaths = lappend(indexpaths, ipath); - - if (ipath->indexinfo->amhasgetbitmap) - bitindexpaths = lappend(bitindexpaths, ipath); - } - - /* - * Generate BitmapOrPaths for any suitable OR-clauses present in the - * clause list. - */ - bitindexpaths = list_concat(bitindexpaths, - generate_bitmap_or_paths(root, rel, - clause_list, NIL, - outer_rel)); - - /* - * Likewise, generate paths using executor-managed ScalarArrayOpExpr - * clauses; these can't be simple indexscans but they can be used in - * bitmap scans. - */ - bitindexpaths = list_concat(bitindexpaths, - find_saop_paths(root, rel, - clause_list, NIL, - false, outer_rel)); - - /* - * 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, outer_rel); - bpath = create_bitmap_heap_path(root, rel, bitmapqual, outer_rel); - indexpaths = lappend(indexpaths, bpath); - } - - /* - * Now choose the cheapest members of indexpaths. - */ - if (indexpaths != NIL) - { - *cheapest_startup = *cheapest_total = (Path *) linitial(indexpaths); - - for_each_cell(l, lnext(list_head(indexpaths))) - { - Path *path = (Path *) lfirst(l); - - if (compare_path_costs(path, *cheapest_startup, STARTUP_COST) < 0) - *cheapest_startup = path; - if (compare_path_costs(path, *cheapest_total, TOTAL_COST) < 0) - *cheapest_total = path; - } - } - - /* Cache the results --- whether positive or negative */ - info = makeNode(InnerIndexscanInfo); - info->other_relids = outer_relids; - info->isouterjoin = isouterjoin; - info->cheapest_startup_innerpath = *cheapest_startup; - info->cheapest_total_innerpath = *cheapest_total; - rel->index_inner_paths = lcons(info, rel->index_inner_paths); - - MemoryContextSwitchTo(oldcontext); -} - -/* - * find_clauses_for_join - * Generate a list of clauses that are potentially useful for - * scanning rel as the inner side of a nestloop join. - * - * We consider both join and restriction clauses. Any joinclause that uses - * only otherrels in the specified outer_relids is fair game. But there must - * be at least one such joinclause in the final list, otherwise we return NIL - * indicating that there isn't any potential win here. - */ -static List * -find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel, - Relids outer_relids, bool isouterjoin) -{ - List *clause_list = NIL; - Relids join_relids; - ListCell *l; - - /* - * Look for joinclauses that are usable with given outer_relids. Note - * we'll take anything that's applicable to the join whether it has - * anything to do with an index or not; since we're only building a list, - * it's not worth filtering more finely here. - */ - join_relids = bms_union(rel->relids, outer_relids); - - foreach(l, rel->joininfo) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - - /* Can't use pushed-down join clauses in outer join */ - if (isouterjoin && rinfo->is_pushed_down) - continue; - if (!bms_is_subset(rinfo->required_relids, join_relids)) - continue; - - clause_list = lappend(clause_list, rinfo); - } - - bms_free(join_relids); - - /* - * Also check to see if any EquivalenceClasses can produce a relevant - * joinclause. Since all such clauses are effectively pushed-down, this - * doesn't apply to outer joins. - */ - if (!isouterjoin && rel->has_eclass_joins) - clause_list = list_concat(clause_list, - find_eclass_clauses_for_index_join(root, - rel, - outer_relids)); - - /* If no join clause was matched then forget it, per comments above */ - if (clause_list == NIL) - return NIL; + if (index->relam == BTREE_AM_OID && + !list_member_oid(ec->ec_opfamilies, curFamily)) + return false; - /* We can also use any plain restriction clauses for the rel */ - clause_list = list_concat(list_copy(rel->baserestrictinfo), clause_list); + /* We insist on collation match for all index types, though */ + if (!IndexCollMatchesExprColl(curCollation, ec->ec_collation)) + return false; - return clause_list; + return match_index_to_operand((Node *) em->em_expr, indexcol, index); } /* @@ -2473,6 +2570,8 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel, * * Note that we aren't interested in collations here; the caller must check * for a collation match, if it's dealing with an operator where that matters. + * + * This is exported for use in selfuncs.c. */ bool match_index_to_operand(Node *operand, @@ -2543,7 +2642,7 @@ match_index_to_operand(Node *operand, * ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS ---- ****************************************************************************/ -/*---------- +/* * These routines handle special optimization of operators that can be * used with index scans even though they are not known to the executor's * indexscan machinery. The key idea is that these operators allow us @@ -2590,7 +2689,6 @@ match_index_to_operand(Node *operand, * the index's opfamily this transformation is a no-op, but clauses recognized * by match_special_index_operator() or match_boolean_index_clause() must be * converted into one or more "regular" indexqual conditions. - *---------- */ /* @@ -3168,9 +3266,7 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, /* * See how many of the remaining columns match some index column in the - * same way. A note about rel membership tests: we assume that the clause - * as a whole is already known to use only Vars from the indexed relation - * and possibly some acceptable outer relations. So the "other" side of + * same way. As in match_clause_to_indexcol(), the "other" side of * any potential index condition is OK as long as it doesn't use Vars from * the indexed relation. */ |