diff options
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 21 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 89 |
2 files changed, 110 insertions, 0 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 7812a8628f..e480797ca8 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -110,6 +110,7 @@ Cost disable_cost = 1.0e10; bool enable_seqscan = true; bool enable_indexscan = true; +bool enable_indexonlyscan = true; bool enable_bitmapscan = true; bool enable_tidscan = true; bool enable_sort = true; @@ -119,6 +120,9 @@ bool enable_material = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +/* Possibly this should become a GUC too */ +static double visibility_fraction = 0.9; + typedef struct { PlannerInfo *root; @@ -210,6 +214,7 @@ cost_seqscan(Path *path, PlannerInfo *root, * 'index' is the index to be used * 'indexQuals' is the list of applicable qual clauses (implicit AND semantics) * 'indexOrderBys' is the list of ORDER BY operators for amcanorderbyop indexes + * 'indexonly' is true if it's an index-only scan * 'outer_rel' is the outer relation when we are considering using the index * scan as the inside of a nestloop join (hence, some of the indexQuals * are join clauses, and we should expect repeated scans of the index); @@ -232,6 +237,7 @@ cost_index(IndexPath *path, PlannerInfo *root, IndexOptInfo *index, List *indexQuals, List *indexOrderBys, + bool indexonly, RelOptInfo *outer_rel) { RelOptInfo *baserel = index->rel; @@ -314,6 +320,12 @@ cost_index(IndexPath *path, PlannerInfo *root, * For partially-correlated indexes, we ought to charge somewhere between * these two estimates. We currently interpolate linearly between the * estimates based on the correlation squared (XXX is that appropriate?). + * + * If it's an index-only scan, then we will not need to fetch any heap + * pages for which the visibility map shows all tuples are visible. + * Unfortunately, we have no stats as to how much of the heap is + * all-visible, and that's likely to be a rather unstable number anyway. + * We use an arbitrary constant visibility_fraction to estimate this. *---------- */ if (outer_rel != NULL && outer_rel->rows > 1) @@ -333,6 +345,8 @@ cost_index(IndexPath *path, PlannerInfo *root, (double) index->pages, root); + pages_fetched = ceil(pages_fetched * visibility_fraction); + max_IO_cost = (pages_fetched * spc_random_page_cost) / num_scans; /* @@ -352,6 +366,8 @@ cost_index(IndexPath *path, PlannerInfo *root, (double) index->pages, root); + pages_fetched = ceil(pages_fetched * visibility_fraction); + min_IO_cost = (pages_fetched * spc_random_page_cost) / num_scans; } else @@ -365,11 +381,16 @@ cost_index(IndexPath *path, PlannerInfo *root, (double) index->pages, root); + pages_fetched = ceil(pages_fetched * visibility_fraction); + /* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */ max_IO_cost = pages_fetched * spc_random_page_cost; /* min_IO_cost is for the perfectly correlated case (csquared=1) */ pages_fetched = ceil(indexSelectivity * (double) baserel->pages); + + pages_fetched = ceil(pages_fetched * visibility_fraction); + min_IO_cost = spc_random_page_cost; if (pages_fetched > 1) min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index fc96f4f1da..7090a7e0c0 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -18,6 +18,7 @@ #include <math.h> #include "access/skey.h" +#include "access/sysattr.h" #include "catalog/pg_am.h" #include "catalog/pg_collation.h" #include "catalog/pg_operator.h" @@ -88,6 +89,7 @@ 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 List *group_clauses_by_indexkey(IndexOptInfo *index, List *clauses, List *outer_clauses, Relids outer_relids, @@ -314,6 +316,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, bool useful_predicate; bool found_clause; bool index_is_ordered; + bool index_only_scan = false; + bool checked_index_only = false; /* * Check that index supports the desired scan type(s) @@ -438,6 +442,10 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, */ if (found_clause || useful_pathkeys != NIL || useful_predicate) { + /* First, detect whether index-only scan is possible */ + index_only_scan = check_index_only(rel, index); + checked_index_only = true; + ipath = create_index_path(root, index, restrictclauses, orderbyclauses, @@ -445,6 +453,7 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, index_is_ordered ? ForwardScanDirection : NoMovementScanDirection, + index_only_scan, outer_rel); result = lappend(result, ipath); } @@ -462,11 +471,15 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, index_pathkeys); if (useful_pathkeys != NIL) { + if (!checked_index_only) + index_only_scan = check_index_only(rel, index); + ipath = create_index_path(root, index, restrictclauses, NIL, useful_pathkeys, BackwardScanDirection, + index_only_scan, outer_rel); result = lappend(result, ipath); } @@ -1040,6 +1053,82 @@ find_list_position(Node *node, List **nodelist) } +/* + * check_index_only + * Determine whether an index-only scan is possible for this index. + */ +static bool +check_index_only(RelOptInfo *rel, IndexOptInfo *index) +{ + bool result; + Bitmapset *attrs_used = NULL; + Bitmapset *index_attrs = NULL; + ListCell *lc; + int i; + + /* Index-only scans must be enabled, and AM must be capable of it */ + if (!enable_indexonlyscan) + return false; + if (!index->amcanreturn) + return false; + + /* + * Check that all needed attributes of the relation are available from + * the index. + * + * XXX this is overly conservative for partial indexes, since we will + * consider attributes involved in the index predicate as required even + * though the predicate won't need to be checked at runtime. (The same + * is true for attributes used only in index quals, if we are certain + * that the index is not lossy.) However, it would be quite expensive + * to determine that accurately at this point, so for now we take the + * easy way out. + */ + + /* + * Add all the attributes needed for joins or final output. Note: we must + * look at reltargetlist, not the attr_needed data, because attr_needed + * isn't computed for inheritance child rels. + */ + pull_varattnos((Node *) rel->reltargetlist, rel->relid, &attrs_used); + + /* Add all the attributes used by restriction clauses. */ + foreach(lc, rel->baserestrictinfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used); + } + + /* Construct a bitmapset of columns stored in the index. */ + for (i = 0; i < index->ncolumns; i++) + { + int attno = index->indexkeys[i]; + + /* + * For the moment, we just ignore index expressions. It might be nice + * to do something with them, later. We also ignore index columns + * that are system columns (such as OID), because the virtual-tuple + * coding used by IndexStoreHeapTuple() can't deal with them. + */ + if (attno <= 0) + continue; + + index_attrs = + bms_add_member(index_attrs, + attno - FirstLowInvalidHeapAttributeNumber); + } + + /* Do we have all the necessary attributes? */ + result = bms_is_subset(attrs_used, index_attrs); + + bms_free(attrs_used); + bms_free(index_attrs); + + return result; +} + + /**************************************************************************** * ---- ROUTINES TO CHECK RESTRICTIONS ---- ****************************************************************************/ |