diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 2952bfb..6abeda1 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -25,6 +25,7 @@ #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" @@ -130,7 +131,13 @@ static PathClauseUsage *classify_index_clause_usage(Path *path, static Relids get_bitmap_tree_required_outer(Path *bitmapqual); 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 bool check_index_only(PlannerInfo *root, RelOptInfo *rel, IndexOptInfo *index); +static bool check_index_only_targetlist(PlannerInfo *root, + RelOptInfo *rel, + IndexOptInfo *index, + Bitmapset *index_canreturn_attrs); +static bool check_index_only_clauses(List *clauses, + IndexOptInfo *index); static double get_loop_count(PlannerInfo *root, Index cur_relid, Relids outer_relids); static double adjust_rowcount_for_semijoins(PlannerInfo *root, Index cur_relid, @@ -1020,7 +1027,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, * index data retrieval anyway. */ index_only_scan = (scantype != ST_BITMAPSCAN && - check_index_only(rel, index)); + check_index_only(root, rel, index)); /* * 4. Generate an indexscan path if there are relevant restriction clauses @@ -1786,7 +1793,7 @@ find_list_position(Node *node, List **nodelist) * Determine whether an index-only scan is possible for this index. */ static bool -check_index_only(RelOptInfo *rel, IndexOptInfo *index) +check_index_only(PlannerInfo *root, RelOptInfo *rel, IndexOptInfo *index) { bool result; Bitmapset *attrs_used = NULL; @@ -1837,8 +1844,10 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) int attno = index->indexkeys[i]; /* - * For the moment, we just ignore index expressions. It might be nice - * to do something with them, later. + * We ignore expressions here and only look at plain attributes. Analyzing + * expressions is also a bit more expensive, so we first try to match the + * expressions to attributes indexed directly, and spend additional CPU time + * on analyzing the expressions only if needed. */ if (attno == 0) continue; @@ -1852,12 +1861,206 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) /* Do we have all the necessary attributes? */ result = bms_is_subset(attrs_used, index_canreturn_attrs); + /* + * If the directly indexed attributes are not sufficient, it's time to look + * at index expressions and try to match them to targetlist and index clauses. + */ + if (!result && index->indexprs) + result = + check_index_only_targetlist(root, rel, index, index_canreturn_attrs) && + check_index_only_clauses(index->indrestrictinfo, index); + bms_free(attrs_used); bms_free(index_canreturn_attrs); return result; } +typedef struct check_index_only_walker_ctx +{ + IndexOptInfo *index; + Bitmapset *index_canreturn_attrs; /* attributes that can be returned + * by index */ + bool match; /* TRUE if expression matches + * index */ +} check_index_only_walker_ctx; + +/* + * check_index_only_expr_walker + * Recursive walker that checks if each part of the expression can be + * build with index expressions or attributes + */ +static bool +check_index_only_expr_walker(Node *node, check_index_only_walker_ctx *ctx) +{ + int i; + + /* + * If node is empty or some subexpression has already reported that it + * isn't match the index then quit looking any further + */ + if (node == NULL || ctx->match == false) + return false; + + /* If this is a Var then check if it can be returned by the index */ + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varno == ctx->index->rel->relid && + !bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber, + ctx->index_canreturn_attrs)) + ctx->match = false; + return false; + } + + /* + * Not a Var, so it has to be an expression. So we try to match it to + * index expressions. If it matches, we're done with this subtree. + * + * XXX This also check the expression against indexed attributes, but + * that seems a bit pointless as that can only match Vars, which we + * already handled above. It's a fairly cheap check, but maybe on + * indexes with many columns it might be noticeable (and only looking + * at index->indexprs would help?). + */ + for (i = 0; i < ctx->index->ncolumns; i++) + if (match_index_to_operand(node, i, ctx->index)) + return false; + + /* Recursive check */ + return expression_tree_walker(node, check_index_only_expr_walker, (void *) ctx); +} + +/* + * check_index_only_targetlist + * Checks that every target of index->relation can be returned by index + * + * In this function we're looking through root->parse->targetlist and pick + * those targets which contain index->relation's attributes. For each selected + * target we use recursive function check_index_only_expr_walker() to check if + * target can be build with the index expressions and attributes and none of + * the other index->relation's attributes + */ +static bool +check_index_only_targetlist(PlannerInfo *root, + RelOptInfo *rel, + IndexOptInfo *index, + Bitmapset *index_canreturn_attrs) +{ + ListCell *lc; + bool result = true; + + /* Check expressions from targetlist */ + foreach(lc, root->parse->targetList) + { + TargetEntry *te = (TargetEntry *) lfirst(lc); + Bitmapset *varnos = pull_varnos((Node *) te->expr); + + /* Ignore targetlist entries not related to the indexed relation. */ + if (bms_is_member(rel->relid, varnos)) + { + bool is_matched = false; /* assume the expression is not matched */ + Bitmapset *attrs = NULL; + + pull_varattnos((Node *) te->expr, rel->relid, &attrs); + + if (bms_is_subset(attrs, index_canreturn_attrs)) + is_matched = true; + else + { + check_index_only_walker_ctx *ctx; + + ctx = (check_index_only_walker_ctx *) palloc(sizeof(check_index_only_walker_ctx)); + ctx->index = index; + ctx->match = true; + ctx->index_canreturn_attrs = index_canreturn_attrs; + check_index_only_expr_walker((Node *) te->expr, ctx); + + if (ctx->match) + is_matched = true; + + pfree(ctx); + } + + bms_free(attrs); + + /* if the expression is not matched, we don't need to look any further */ + if (! is_matched) + { + result = false; + break; + } + } + + bms_free(varnos); + } + + return result; +} + +/* + * check_index_only_clauses + * Recursive function that checks that each clause matches the index + * + * clauses is a list of RestrictInfos or a BoolExpr containing RestrictInfos + */ +static bool +check_index_only_clauses(List *clauses, + IndexOptInfo *index) +{ + int indexcol; + ListCell *lc; + + foreach(lc, clauses) + { + bool match = false; + Node *node = (Node *) lfirst(lc); + + /* + * We expect here either a RestrictInfo or a boolean + * expression containing other RestrictInfos + */ + if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + + if (!restriction_is_or_clause(rinfo)) + { + for (indexcol = 0; indexcol < index->ncolumns; indexcol++) + { + if (match_clause_to_indexcol(index, indexcol, rinfo)) + { + match = true; + break; + } + } + } + else + { + List *subclauses = ((BoolExpr *) rinfo->orclause)->args; + + match = check_index_only_clauses(subclauses, index); + } + } + else if (IsA(node, BoolExpr)) + { + BoolExpr *boolexpr = (BoolExpr *) node; + + match = check_index_only_clauses(boolexpr->args, index); + } + + /* + * If at least one restriction doesn't match index then return false + */ + if (!match) + return false; + } + + /* If we got here then everything's fine */ + return true; +} + /* * get_loop_count * Choose the loop count estimate to use for costing a parameterized path