diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 21 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 89 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 1 | ||||
-rw-r--r-- | src/backend/optimizer/util/var.c | 43 |
7 files changed, 153 insertions, 20 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 7812a8628fc..e480797ca8e 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 fc96f4f1dac..7090a7e0c0d 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 ---- ****************************************************************************/ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 0dedebaf705..36ee7c5648a 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -95,7 +95,7 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, List *indexorderby, List *indexorderbyorig, - ScanDirection indexscandir); + ScanDirection indexscandir, bool indexonly); static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig); @@ -1183,7 +1183,8 @@ create_indexscan_plan(PlannerInfo *root, stripped_indexquals, fixed_indexorderbys, indexorderbys, - best_path->indexscandir); + best_path->indexscandir, + best_path->indexonly); copy_path_costsize(&scan_plan->scan.plan, &best_path->path); /* use the indexscan-specific rows estimate, not the parent rel's */ @@ -2841,7 +2842,8 @@ make_indexscan(List *qptlist, List *indexqualorig, List *indexorderby, List *indexorderbyorig, - ScanDirection indexscandir) + ScanDirection indexscandir, + bool indexonly) { IndexScan *node = makeNode(IndexScan); Plan *plan = &node->scan.plan; @@ -2858,6 +2860,7 @@ make_indexscan(List *qptlist, node->indexorderby = indexorderby; node->indexorderbyorig = indexorderbyorig; node->indexorderdir = indexscandir; + node->indexonly = indexonly; return node; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b4982746a2e..5c18b72344d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3297,7 +3297,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) /* Estimate the cost of index scan */ indexScanPath = create_index_path(root, indexInfo, NIL, NIL, NIL, - ForwardScanDirection, NULL); + ForwardScanDirection, false, NULL); return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 4a1c94affbd..8ed55a3d0e2 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -25,8 +25,8 @@ #include "optimizer/paths.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" -#include "utils/selfuncs.h" #include "utils/lsyscache.h" +#include "utils/selfuncs.h" static List *translate_sub_tlist(List *tlist, int relid); @@ -418,6 +418,7 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel) * 'indexscandir' is ForwardScanDirection or BackwardScanDirection * for an ordered index, or NoMovementScanDirection for * an unordered index. + * 'indexonly' is true if an index-only scan is wanted. * 'outer_rel' is the outer relation if this is a join inner indexscan path. * (pathkeys and indexscandir are ignored if so.) NULL if not. * @@ -430,6 +431,7 @@ create_index_path(PlannerInfo *root, List *indexorderbys, List *pathkeys, ScanDirection indexscandir, + bool indexonly, RelOptInfo *outer_rel) { IndexPath *pathnode = makeNode(IndexPath); @@ -468,6 +470,7 @@ create_index_path(PlannerInfo *root, pathnode->isjoininner = (outer_rel != NULL); pathnode->indexscandir = indexscandir; + pathnode->indexonly = indexonly; if (outer_rel != NULL) { @@ -506,7 +509,8 @@ create_index_path(PlannerInfo *root, pathnode->rows = rel->rows; } - cost_index(pathnode, root, index, indexquals, indexorderbys, outer_rel); + cost_index(pathnode, root, index, indexquals, indexorderbys, + indexonly, outer_rel); return pathnode; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 8a3a5d85e2a..742e7a880ad 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -210,6 +210,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->relam = indexRelation->rd_rel->relam; info->amcostestimate = indexRelation->rd_am->amcostestimate; info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop; + info->amcanreturn = indexRelation->rd_am->amcanreturn; info->amoptionalkey = indexRelation->rd_am->amoptionalkey; info->amsearchnulls = indexRelation->rd_am->amsearchnulls; info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple); diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 8ce7ee41a18..888f6f67a84 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -36,6 +36,12 @@ typedef struct typedef struct { + Bitmapset *varattnos; + Index varno; +} pull_varattnos_context; + +typedef struct +{ int var_location; int sublevels_up; } locate_var_of_level_context; @@ -70,7 +76,7 @@ typedef struct static bool pull_varnos_walker(Node *node, pull_varnos_context *context); -static bool pull_varattnos_walker(Node *node, Bitmapset **varattnos); +static bool pull_varattnos_walker(Node *node, pull_varattnos_context *context); static bool contain_var_clause_walker(Node *node, void *context); static bool contain_vars_of_level_walker(Node *node, int *sublevels_up); static bool locate_var_of_level_walker(Node *node, @@ -177,23 +183,31 @@ pull_varnos_walker(Node *node, pull_varnos_context *context) * pull_varattnos * Find all the distinct attribute numbers present in an expression tree, * and add them to the initial contents of *varattnos. - * Only Vars that reference RTE 1 of rtable level zero are considered. + * Only Vars of the given varno and rtable level zero are considered. * * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that * we can include system attributes (e.g., OID) in the bitmap representation. * - * Currently, this does not support subqueries nor expressions containing - * references to multiple tables; not needed since it's only applied to - * index expressions and predicates. + * Currently, this does not support unplanned subqueries; that is not needed + * for current uses. It will handle already-planned SubPlan nodes, though, + * looking into only the "testexpr" and the "args" list. (The subplan cannot + * contain any other references to Vars of the current level.) */ void -pull_varattnos(Node *node, Bitmapset **varattnos) +pull_varattnos(Node *node, Index varno, Bitmapset **varattnos) { - (void) pull_varattnos_walker(node, varattnos); + pull_varattnos_context context; + + context.varattnos = *varattnos; + context.varno = varno; + + (void) pull_varattnos_walker(node, &context); + + *varattnos = context.varattnos; } static bool -pull_varattnos_walker(Node *node, Bitmapset **varattnos) +pull_varattnos_walker(Node *node, pull_varattnos_context *context) { if (node == NULL) return false; @@ -201,17 +215,18 @@ pull_varattnos_walker(Node *node, Bitmapset **varattnos) { Var *var = (Var *) node; - Assert(var->varno == 1); - *varattnos = bms_add_member(*varattnos, - var->varattno - FirstLowInvalidHeapAttributeNumber); + if (var->varno == context->varno && var->varlevelsup == 0) + context->varattnos = + bms_add_member(context->varattnos, + var->varattno - FirstLowInvalidHeapAttributeNumber); return false; } - /* Should not find a subquery or subplan */ + + /* Should not find an unplanned subquery */ Assert(!IsA(node, Query)); - Assert(!IsA(node, SubPlan)); return expression_tree_walker(node, pull_varattnos_walker, - (void *) varattnos); + (void *) context); } |