diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 2952bfb..e81f914 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" @@ -78,6 +79,14 @@ typedef struct int indexcol; /* index column we want to match to */ } ec_member_matches_arg; +/* Context for index only expression checker */ +typedef struct +{ + IndexOptInfo *index; + bool match; /* TRUE if expression matches + * index */ +} check_index_only_walker_ctx; + static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel, IndexOptInfo *index, @@ -130,7 +139,15 @@ 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 bool check_index_only_expr_walker(Node *node, check_index_only_walker_ctx *ctx); +static bool check_index_only_expr(Node *expr, 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 +1037,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 +1803,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 +1854,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,6 +1871,15 @@ 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); @@ -1859,6 +1887,225 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) } /* + * 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; + + /* + * Try to match current node to index expressions. If it matches, we're + * done with this subtree + */ + for (i = 0; i < ctx->index->ncolumns; i++) + if (match_index_to_operand(node, i, ctx->index)) + return false; + + /* + * If after all we still have an unresolved Var that doesn't match index + * then we cannot use index only scan + */ + if (IsA(node, Var) && ((Var *) node)->varno == ctx->index->rel->relid) + { + ctx->match = false; + return false; + } + + /* Recursive check */ + return expression_tree_walker(node, check_index_only_expr_walker, (void *) ctx); +} + +/* + * check_index_only_expr + * Runs check_index_only_expr_walker() to make sure that expression could + * be build with index expressions and attributes + */ +static bool +check_index_only_expr(Node *expr, IndexOptInfo *index) +{ + bool flag = false; + check_index_only_walker_ctx *ctx; + + /* Don't bother to run walker for NULL expressions */ + if (!expr) + return true; + + ctx = (check_index_only_walker_ctx *) palloc(sizeof(check_index_only_walker_ctx)); + ctx->index = index; + ctx->match = true; + check_index_only_expr_walker(expr, ctx); + + if (ctx->match) + flag = true; + + pfree(ctx); + + return flag; +} + +/* + * 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) || + check_index_only_expr((Node *) te->expr, index)) + { + is_matched = true; + } + + 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) +{ + 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; + Expr *clause = rinfo->clause; + + if (!restriction_is_or_clause(rinfo)) + { + /* + * Clause could be a binary opclause, ScalarArrayOpExpr, + * RowCompareExpr or NullTest. We take left and right parts of + * binary clauses or single argument of NullTest and make sure + * that they match index. Unlike match_clause_to_indexcol() + * we do not care here about operator family + */ + Node *left = NULL; + Node *right = NULL; + + if (is_opclause(clause)) + { + left = get_leftop(clause); + right = get_rightop(clause); + } + else if (IsA(clause, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; + + left = (Node *) linitial(saop->args); + right = (Node *) lsecond(saop->args); + } + else if (IsA(clause, RowCompareExpr)) + { + RowCompareExpr *rowexpr = (RowCompareExpr *) clause; + + left = (Node *) rowexpr->largs; + right = (Node *) rowexpr->rargs; + } + else if (index->amsearchnulls && IsA(clause, NullTest)) + { + NullTest *nt = (NullTest *) clause; + + left = (Node *) nt->arg; + /* Leave right part equal to NULL */ + } + + if (check_index_only_expr(left, index) + && check_index_only_expr(right, index)) + { + 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 * with the given set of outer relids.