diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/nodes/nodeFuncs.c | 22 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 17 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 43 | ||||
-rw-r--r-- | src/backend/optimizer/path/equivclass.c | 37 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 404 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 332 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 65 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 17 | ||||
-rw-r--r-- | src/backend/optimizer/util/restrictinfo.c | 64 | ||||
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 149 |
11 files changed, 602 insertions, 550 deletions
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 2385d026026..8ed30c011a6 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -2192,6 +2192,17 @@ expression_tree_walker(Node *node, /* groupClauses are deemed uninteresting */ } break; + case T_IndexClause: + { + IndexClause *iclause = (IndexClause *) node; + + if (walker(iclause->rinfo, context)) + return true; + if (expression_tree_walker((Node *) iclause->indexquals, + walker, context)) + return true; + } + break; case T_PlaceHolderVar: return walker(((PlaceHolderVar *) node)->phexpr, context); case T_InferenceElem: @@ -2999,6 +3010,17 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_IndexClause: + { + IndexClause *iclause = (IndexClause *) node; + IndexClause *newnode; + + FLATCOPY(newnode, iclause, IndexClause); + MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *); + MUTATE(newnode->indexquals, iclause->indexquals, List *); + return (Node *) newnode; + } + break; case T_PlaceHolderVar: { PlaceHolderVar *phv = (PlaceHolderVar *) node; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f97cf37f1f8..10038a22cf9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1744,8 +1744,6 @@ _outIndexPath(StringInfo str, const IndexPath *node) WRITE_NODE_FIELD(indexinfo); WRITE_NODE_FIELD(indexclauses); - WRITE_NODE_FIELD(indexquals); - WRITE_NODE_FIELD(indexqualcols); WRITE_NODE_FIELD(indexorderbys); WRITE_NODE_FIELD(indexorderbycols); WRITE_ENUM_FIELD(indexscandir, ScanDirection); @@ -2448,6 +2446,18 @@ _outRestrictInfo(StringInfo str, const RestrictInfo *node) } static void +_outIndexClause(StringInfo str, const IndexClause *node) +{ + WRITE_NODE_TYPE("INDEXCLAUSE"); + + WRITE_NODE_FIELD(rinfo); + WRITE_NODE_FIELD(indexquals); + WRITE_BOOL_FIELD(lossy); + WRITE_INT_FIELD(indexcol); + WRITE_NODE_FIELD(indexcols); +} + +static void _outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node) { WRITE_NODE_TYPE("PLACEHOLDERVAR"); @@ -4044,6 +4054,9 @@ outNode(StringInfo str, const void *obj) case T_RestrictInfo: _outRestrictInfo(str, obj); break; + case T_IndexClause: + _outIndexClause(str, obj); + break; case T_PlaceHolderVar: _outPlaceHolderVar(str, obj); break; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index b8d406f230c..1057ddaa3e5 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -145,7 +145,7 @@ typedef struct QualCost total; } cost_qual_eval_context; -static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals); +static List *extract_nonindex_conditions(List *qual_clauses, List *indexclauses); static MergeScanSelCache *cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey); @@ -517,18 +517,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, { path->path.rows = path->path.param_info->ppi_rows; /* qpquals come from the rel's restriction clauses and ppi_clauses */ - qpquals = list_concat( - extract_nonindex_conditions(path->indexinfo->indrestrictinfo, - path->indexquals), + qpquals = list_concat(extract_nonindex_conditions(path->indexinfo->indrestrictinfo, + path->indexclauses), extract_nonindex_conditions(path->path.param_info->ppi_clauses, - path->indexquals)); + path->indexclauses)); } else { path->path.rows = baserel->rows; /* qpquals come from just the rel's restriction clauses */ qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo, - path->indexquals); + path->indexclauses); } if (!enable_indexscan) @@ -753,20 +752,19 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, * * Given a list of quals to be enforced in an indexscan, extract the ones that * will have to be applied as qpquals (ie, the index machinery won't handle - * them). The actual rules for this appear in create_indexscan_plan() in - * createplan.c, but the full rules are fairly expensive and we don't want to - * go to that much effort for index paths that don't get selected for the - * final plan. So we approximate it as quals that don't appear directly in - * indexquals and also are not redundant children of the same EquivalenceClass - * as some indexqual. This method neglects some infrequently-relevant - * considerations, specifically clauses that needn't be checked because they - * are implied by an indexqual. It does not seem worth the cycles to try to - * factor that in at this stage, even though createplan.c will take pains to - * remove such unnecessary clauses from the qpquals list if this path is - * selected for use. + * them). Here we detect only whether a qual clause is directly redundant + * with some indexclause. If the index path is chosen for use, createplan.c + * will try a bit harder to get rid of redundant qual conditions; specifically + * it will see if quals can be proven to be implied by the indexquals. But + * it does not seem worth the cycles to try to factor that in at this stage, + * since we're only trying to estimate qual eval costs. Otherwise this must + * match the logic in create_indexscan_plan(). + * + * qual_clauses, and the result, are lists of RestrictInfos. + * indexclauses is a list of IndexClauses. */ static List * -extract_nonindex_conditions(List *qual_clauses, List *indexquals) +extract_nonindex_conditions(List *qual_clauses, List *indexclauses) { List *result = NIL; ListCell *lc; @@ -777,10 +775,8 @@ extract_nonindex_conditions(List *qual_clauses, List *indexquals) if (rinfo->pseudoconstant) continue; /* we may drop pseudoconstants here */ - if (list_member_ptr(indexquals, rinfo)) - continue; /* simple duplicate */ - if (is_redundant_derived_clause(rinfo, indexquals)) - continue; /* derived from same EquivalenceClass */ + if (is_redundant_with_indexclauses(rinfo, indexclauses)) + continue; /* dup or derived from same EquivalenceClass */ /* ... skip the predicate proof attempt createplan.c will try ... */ result = lappend(result, rinfo); } @@ -4242,8 +4238,7 @@ has_indexed_join_quals(NestPath *joinpath) innerpath->parent->relids, joinrelids)) { - if (!(list_member_ptr(indexclauses, rinfo) || - is_redundant_derived_clause(rinfo, indexclauses))) + if (!is_redundant_with_indexclauses(rinfo, indexclauses)) return false; found_one = true; } diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 3454f129122..23792508b7b 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -2511,3 +2511,40 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist) return false; } + +/* + * is_redundant_with_indexclauses + * Test whether rinfo is redundant with any clause in the IndexClause + * list. Here, for convenience, we test both simple identity and + * whether it is derived from the same EC as any member of the list. + */ +bool +is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses) +{ + EquivalenceClass *parent_ec = rinfo->parent_ec; + ListCell *lc; + + foreach(lc, indexclauses) + { + IndexClause *iclause = lfirst_node(IndexClause, lc); + RestrictInfo *otherrinfo = iclause->rinfo; + + /* If indexclause is lossy, it won't enforce the condition exactly */ + if (iclause->lossy) + continue; + + /* Match if it's same clause (pointer equality should be enough) */ + if (rinfo == otherrinfo) + return true; + /* Match if derived from same EC */ + if (parent_ec && otherrinfo->parent_ec == parent_ec) + return true; + + /* + * No need to look at the derived clauses in iclause->indexquals; they + * couldn't match if the parent clause didn't. + */ + } + + return false; +} diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 7e1a3908f12..51d2da56832 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -56,7 +56,7 @@ typedef enum typedef struct { bool nonempty; /* True if lists are not all empty */ - /* Lists of RestrictInfos, one per index column */ + /* Lists of IndexClause nodes, one list per index column */ List *indexclauses[INDEX_MAX_KEYS]; } IndexClauseSet; @@ -175,13 +175,19 @@ static bool match_boolean_index_clause(Node *clause, int indexcol, static bool match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation, bool indexkey_on_left); +static IndexClause *expand_indexqual_conditions(IndexOptInfo *index, + int indexcol, + RestrictInfo *rinfo); static Expr *expand_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index); static List *expand_indexqual_opclause(RestrictInfo *rinfo, - Oid opfamily, Oid idxcollation); + Oid opfamily, Oid idxcollation, + bool *lossy); static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo, IndexOptInfo *index, - int indexcol); + int indexcol, + List **indexcolnos, + bool *lossy); static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation, Const *prefix, Pattern_Prefix_Status pstatus); static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, @@ -496,7 +502,7 @@ consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel, * * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and * 'bitindexpaths' as above - * 'indexjoinclauses' is a list of RestrictInfos for join clauses + * 'indexjoinclauses' is a list of IndexClauses for join clauses * 'considered_clauses' is the total number of clauses considered (so far) * '*considered_relids' is a list of all relids sets already considered */ @@ -516,8 +522,9 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel, /* Examine relids of each joinclause in the given list */ foreach(lc, indexjoinclauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - Relids clause_relids = rinfo->clause_relids; + IndexClause *iclause = (IndexClause *) lfirst(lc); + Relids clause_relids = iclause->rinfo->clause_relids; + EquivalenceClass *parent_ec = iclause->rinfo->parent_ec; ListCell *lc2; /* If we already tried its relids set, no need to do so again */ @@ -558,8 +565,8 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel, * parameterization; so skip if any clause derived from the same * eclass would already have been included when using oldrelids. */ - if (rinfo->parent_ec && - eclass_already_used(rinfo->parent_ec, oldrelids, + if (parent_ec && + eclass_already_used(parent_ec, oldrelids, indexjoinclauses)) continue; @@ -628,11 +635,11 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel, /* First find applicable simple join clauses */ foreach(lc, jclauseset->indexclauses[indexcol]) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + IndexClause *iclause = (IndexClause *) lfirst(lc); - if (bms_is_subset(rinfo->clause_relids, relids)) + if (bms_is_subset(iclause->rinfo->clause_relids, relids)) clauseset.indexclauses[indexcol] = - lappend(clauseset.indexclauses[indexcol], rinfo); + lappend(clauseset.indexclauses[indexcol], iclause); } /* @@ -643,12 +650,12 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel, */ foreach(lc, eclauseset->indexclauses[indexcol]) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + IndexClause *iclause = (IndexClause *) lfirst(lc); - if (bms_is_subset(rinfo->clause_relids, relids)) + if (bms_is_subset(iclause->rinfo->clause_relids, relids)) { clauseset.indexclauses[indexcol] = - lappend(clauseset.indexclauses[indexcol], rinfo); + lappend(clauseset.indexclauses[indexcol], iclause); break; } } @@ -688,7 +695,8 @@ eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids, foreach(lc, indexjoinclauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + IndexClause *iclause = (IndexClause *) lfirst(lc); + RestrictInfo *rinfo = iclause->rinfo; if (rinfo->parent_ec == parent_ec && bms_is_subset(rinfo->clause_relids, oldrelids)) @@ -848,7 +856,7 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel, * * '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) + * 'clauses' is the collection of indexable clauses (IndexClause nodes) * 'useful_predicate' indicates whether the index has a useful predicate * 'scantype' indicates whether we need plain or bitmap scan support * 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't @@ -865,7 +873,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, List *result = NIL; IndexPath *ipath; List *index_clauses; - List *clause_columns; Relids outer_relids; double loop_count; List *orderbyclauses; @@ -897,14 +904,12 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, } /* - * 1. Collect the index clauses into a single list. + * 1. Combine the per-column IndexClause lists into an overall 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. + * In the resulting list, 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 list can be empty, if the + * index AM allows that. * * found_lower_saop_clause is set true if we accept a ScalarArrayOpExpr * index clause for a non-first index column. This prevents us from @@ -918,7 +923,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, * otherwise accounted for. */ index_clauses = NIL; - clause_columns = NIL; found_lower_saop_clause = false; outer_relids = bms_copy(rel->lateral_relids); for (indexcol = 0; indexcol < index->ncolumns; indexcol++) @@ -927,8 +931,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, foreach(lc, clauses->indexclauses[indexcol]) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + IndexClause *iclause = (IndexClause *) lfirst(lc); + RestrictInfo *rinfo = iclause->rinfo; + /* We might need to omit ScalarArrayOpExpr clauses */ if (IsA(rinfo->clause, ScalarArrayOpExpr)) { if (!index->amsearcharray) @@ -953,8 +959,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, found_lower_saop_clause = true; } } - index_clauses = lappend(index_clauses, rinfo); - clause_columns = lappend_int(clause_columns, indexcol); + + /* OK to include this clause */ + index_clauses = lappend(index_clauses, iclause); outer_relids = bms_add_members(outer_relids, rinfo->clause_relids); } @@ -1036,7 +1043,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, index_clauses, - clause_columns, orderbyclauses, orderbyclausecols, useful_pathkeys, @@ -1059,7 +1065,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, index_clauses, - clause_columns, orderbyclauses, orderbyclausecols, useful_pathkeys, @@ -1095,7 +1100,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, index_clauses, - clause_columns, NIL, NIL, useful_pathkeys, @@ -1113,7 +1117,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, index_clauses, - clause_columns, NIL, NIL, useful_pathkeys, @@ -1810,7 +1813,7 @@ get_bitmap_tree_required_outer(Path *bitmapqual) * find_indexpath_quals * * Given the Path structure for a plain or bitmap indexscan, extract lists - * of all the indexquals and index predicate conditions used in the Path. + * of all the index clauses and index predicate conditions used in the Path. * These are appended to the initial contents of *quals and *preds (hence * caller should initialize those to NIL). * @@ -1847,8 +1850,14 @@ find_indexpath_quals(Path *bitmapqual, List **quals, List **preds) else if (IsA(bitmapqual, IndexPath)) { IndexPath *ipath = (IndexPath *) bitmapqual; + ListCell *l; - *quals = list_concat(*quals, get_actual_clauses(ipath->indexclauses)); + foreach(l, ipath->indexclauses) + { + IndexClause *iclause = (IndexClause *) lfirst(l); + + *quals = lappend(*quals, iclause->rinfo->clause); + } *preds = list_concat(*preds, list_copy(ipath->indexinfo->indpred)); } else @@ -2239,8 +2248,9 @@ match_clauses_to_index(IndexOptInfo *index, * 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. + * If the clause is usable, add an IndexClause entry for 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 @@ -2277,13 +2287,30 @@ match_clause_to_index(IndexOptInfo *index, /* OK, check each index key column for a match */ for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++) { + ListCell *lc; + + /* Ignore duplicates */ + foreach(lc, clauseset->indexclauses[indexcol]) + { + IndexClause *iclause = (IndexClause *) lfirst(lc); + + if (iclause->rinfo == rinfo) + return; + } + + /* + * XXX this should be changed so that we generate an IndexClause + * immediately upon matching, to avoid repeated work. To-do soon. + */ if (match_clause_to_indexcol(index, indexcol, rinfo)) { + IndexClause *iclause; + + iclause = expand_indexqual_conditions(index, indexcol, rinfo); clauseset->indexclauses[indexcol] = - list_append_unique_ptr(clauseset->indexclauses[indexcol], - rinfo); + lappend(clauseset->indexclauses[indexcol], iclause); clauseset->nonempty = true; return; } @@ -2335,7 +2362,7 @@ match_clause_to_index(IndexOptInfo *index, * target index column. This is sufficient to guarantee that some index * condition can be constructed from the RowCompareExpr --- whether the * remaining columns match the index too is considered in - * adjust_rowcompare_for_index(). + * expand_indexqual_rowcompare(). * * It is also possible to match ScalarArrayOpExpr clauses to indexes, when * the clause is of the form "indexkey op ANY (arrayconst)". @@ -3342,8 +3369,8 @@ match_index_to_operand(Node *operand, * match_boolean_index_clause() similarly detects clauses that can be * converted into boolean equality operators. * - * expand_indexqual_conditions() converts a list of RestrictInfo nodes - * (with implicit AND semantics across list elements) into a list of clauses + * expand_indexqual_conditions() converts a RestrictInfo node + * into an IndexClause, which contains clauses * that the executor can actually handle. For operators that are members of * 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 @@ -3556,38 +3583,28 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation, /* * expand_indexqual_conditions - * Given a list of RestrictInfo nodes, produce a list of directly usable - * index qual clauses. + * Given a RestrictInfo node, create an IndexClause. * * Standard qual clauses (those in the index's opfamily) are passed through * unchanged. Boolean clauses and "special" index operators are expanded * into clauses that the indexscan machinery will know what to do with. * RowCompare clauses are simplified if necessary to create a clause that is * fully checkable by the index. - * - * In addition to the expressions themselves, there are auxiliary lists - * of the index column numbers that the clauses are meant to be used with; - * we generate an updated column number list for the result. (This is not - * the identical list because one input clause sometimes produces more than - * one output clause.) - * - * The input clauses are sorted by column number, and so the output is too. - * (This is depended on in various places in both planner and executor.) */ -void +static IndexClause * expand_indexqual_conditions(IndexOptInfo *index, - List *indexclauses, List *indexclausecols, - List **indexquals_p, List **indexqualcols_p) + int indexcol, + RestrictInfo *rinfo) { + IndexClause *iclause = makeNode(IndexClause); List *indexquals = NIL; - List *indexqualcols = NIL; - ListCell *lcc, - *lci; - forboth(lcc, indexclauses, lci, indexclausecols) + iclause->rinfo = rinfo; + iclause->lossy = false; /* might get changed below */ + iclause->indexcol = indexcol; + iclause->indexcols = NIL; /* might get changed below */ + { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc); - int indexcol = lfirst_int(lci); Expr *clause = rinfo->clause; Oid curFamily; Oid curCollation; @@ -3607,10 +3624,9 @@ expand_indexqual_conditions(IndexOptInfo *index, index); if (boolqual) { - indexquals = lappend(indexquals, - make_simple_restrictinfo(boolqual)); - indexqualcols = lappend_int(indexqualcols, indexcol); - continue; + iclause->indexquals = + list_make1(make_simple_restrictinfo(boolqual)); + return iclause; } } @@ -3620,41 +3636,63 @@ expand_indexqual_conditions(IndexOptInfo *index, */ if (is_opclause(clause)) { - indexquals = list_concat(indexquals, - expand_indexqual_opclause(rinfo, - curFamily, - curCollation)); - /* expand_indexqual_opclause can produce multiple clauses */ - while (list_length(indexqualcols) < list_length(indexquals)) - indexqualcols = lappend_int(indexqualcols, indexcol); + /* + * Check to see if the indexkey is on the right; if so, commute + * the clause. The indexkey should be the side that refers to + * (only) the base relation. + */ + if (!bms_equal(rinfo->left_relids, index->rel->relids)) + { + Oid opno = ((OpExpr *) clause)->opno; + RestrictInfo *newrinfo; + + newrinfo = commute_restrictinfo(rinfo, + get_commutator(opno)); + + /* + * For now, assume it couldn't be any case that requires + * expansion. (This is OK for the current capabilities of + * expand_indexqual_opclause, but we'll need to remove the + * restriction when we open this up for extensions.) + */ + indexquals = list_make1(newrinfo); + } + else + indexquals = expand_indexqual_opclause(rinfo, + curFamily, + curCollation, + &iclause->lossy); } else if (IsA(clause, ScalarArrayOpExpr)) { /* no extra work at this time */ - indexquals = lappend(indexquals, rinfo); - indexqualcols = lappend_int(indexqualcols, indexcol); } else if (IsA(clause, RowCompareExpr)) { - indexquals = lappend(indexquals, - expand_indexqual_rowcompare(rinfo, - index, - indexcol)); - indexqualcols = lappend_int(indexqualcols, indexcol); + RestrictInfo *newrinfo; + + newrinfo = expand_indexqual_rowcompare(rinfo, + index, + indexcol, + &iclause->indexcols, + &iclause->lossy); + if (newrinfo != rinfo) + { + /* We need to report a derived expression */ + indexquals = list_make1(newrinfo); + } } else if (IsA(clause, NullTest)) { Assert(index->amsearchnulls); - indexquals = lappend(indexquals, rinfo); - indexqualcols = lappend_int(indexqualcols, indexcol); } else elog(ERROR, "unsupported indexqual type: %d", (int) nodeTag(clause)); } - *indexquals_p = indexquals; - *indexqualcols_p = indexqualcols; + iclause->indexquals = indexquals; + return iclause; } /* @@ -3725,13 +3763,15 @@ expand_boolean_index_clause(Node *clause, * expand_indexqual_opclause --- expand a single indexqual condition * that is an operator clause * - * The input is a single RestrictInfo, the output a list of RestrictInfos. + * The input is a single RestrictInfo, the output a list of RestrictInfos, + * or NIL if the RestrictInfo's clause can be used as-is. * - * In the base case this is just list_make1(), but we have to be prepared to + * In the base case this is just "return NIL", but we have to be prepared to * expand special cases that were accepted by match_special_index_operator(). */ static List * -expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) +expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation, + bool *lossy) { Expr *clause = rinfo->clause; @@ -3760,6 +3800,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) case OID_BYTEA_LIKE_OP: if (!op_in_opfamily(expr_op, opfamily)) { + *lossy = true; pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll, &prefix, NULL); return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus); @@ -3771,6 +3812,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) case OID_NAME_ICLIKE_OP: if (!op_in_opfamily(expr_op, opfamily)) { + *lossy = true; /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, expr_coll, &prefix, NULL); @@ -3783,6 +3825,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) case OID_NAME_REGEXEQ_OP: if (!op_in_opfamily(expr_op, opfamily)) { + *lossy = true; /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, expr_coll, &prefix, NULL); @@ -3795,6 +3838,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) case OID_NAME_ICREGEXEQ_OP: if (!op_in_opfamily(expr_op, opfamily)) { + *lossy = true; /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, expr_coll, &prefix, NULL); @@ -3806,96 +3850,65 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) case OID_INET_SUBEQ_OP: if (!op_in_opfamily(expr_op, opfamily)) { + *lossy = true; return network_prefix_quals(leftop, expr_op, opfamily, patt->constvalue); } break; } - /* Default case: just make a list of the unmodified indexqual */ - return list_make1(rinfo); + /* Default case: the clause can be used as-is. */ + *lossy = false; + return NIL; } /* * expand_indexqual_rowcompare --- expand a single indexqual condition * that is a RowCompareExpr * - * This is a thin wrapper around adjust_rowcompare_for_index; we export the - * latter so that createplan.c can use it to re-discover which columns of the - * index are used by a row comparison indexqual. - */ -static RestrictInfo * -expand_indexqual_rowcompare(RestrictInfo *rinfo, - IndexOptInfo *index, - int indexcol) -{ - RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause; - Expr *newclause; - List *indexcolnos; - bool var_on_left; - - newclause = adjust_rowcompare_for_index(clause, - index, - indexcol, - &indexcolnos, - &var_on_left); - - /* - * If we didn't have to change the RowCompareExpr, return the original - * RestrictInfo. - */ - if (newclause == (Expr *) clause) - return rinfo; - - /* Else we need a new RestrictInfo */ - return make_simple_restrictinfo(newclause); -} - -/* - * adjust_rowcompare_for_index --- expand a single indexqual condition - * that is a RowCompareExpr - * * It's already known that the first column of the row comparison matches * the specified column of the index. We can use additional columns of the * row comparison as index qualifications, so long as they match the index * in the "same direction", ie, the indexkeys are all on the same side of the * clause and the operators are all the same-type members of the opfamilies. + * * If all the columns of the RowCompareExpr match in this way, we just use it - * as-is. Otherwise, we build a shortened RowCompareExpr (if more than one + * as-is, except for possibly commuting it to put the indexkeys on the left. + * + * Otherwise, we build a shortened RowCompareExpr (if more than one * column matches) or a simple OpExpr (if the first-column match is all * there is). In these cases the modified clause is always "<=" or ">=" * even when the original was "<" or ">" --- this is necessary to match all - * the rows that could match the original. (We are essentially building a - * lossy version of the row comparison when we do this.) + * the rows that could match the original. (We are building a lossy version + * of the row comparison when we do this, so we set *lossy = true.) * * *indexcolnos receives an integer list of the index column numbers (zero - * based) used in the resulting expression. The reason we need to return - * that is that if the index is selected for use, createplan.c will need to - * call this again to extract that list. (This is a bit grotty, but row - * comparison indexquals aren't used enough to justify finding someplace to - * keep the information in the Path representation.) Since createplan.c - * also needs to know which side of the RowCompareExpr is the index side, - * we also return *var_on_left_p rather than re-deducing that there. + * based) used in the resulting expression. We have to pass that back + * because createplan.c will need it. */ -Expr * -adjust_rowcompare_for_index(RowCompareExpr *clause, +static RestrictInfo * +expand_indexqual_rowcompare(RestrictInfo *rinfo, IndexOptInfo *index, int indexcol, List **indexcolnos, - bool *var_on_left_p) + bool *lossy) { + RowCompareExpr *clause = castNode(RowCompareExpr, rinfo->clause); bool var_on_left; int op_strategy; Oid op_lefttype; Oid op_righttype; int matching_cols; Oid expr_op; + List *expr_ops; List *opfamilies; List *lefttypes; List *righttypes; List *new_ops; - ListCell *largs_cell; - ListCell *rargs_cell; + List *var_args; + List *non_var_args; + ListCell *vargs_cell; + ListCell *nargs_cell; ListCell *opnos_cell; ListCell *collids_cell; @@ -3905,7 +3918,17 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, Assert(var_on_left || match_index_to_operand((Node *) linitial(clause->rargs), indexcol, index)); - *var_on_left_p = var_on_left; + + if (var_on_left) + { + var_args = clause->largs; + non_var_args = clause->rargs; + } + else + { + var_args = clause->rargs; + non_var_args = clause->largs; + } expr_op = linitial_oid(clause->opnos); if (!var_on_left) @@ -3918,7 +3941,8 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, /* Initialize returned list of which index columns are used */ *indexcolnos = list_make1_int(indexcol); - /* Build lists of the opfamilies and operator datatypes in case needed */ + /* Build lists of ops, opfamilies and operator datatypes in case needed */ + expr_ops = list_make1_oid(expr_op); opfamilies = list_make1_oid(index->opfamily[indexcol]); lefttypes = list_make1_oid(op_lefttype); righttypes = list_make1_oid(op_righttype); @@ -3930,27 +3954,20 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, * indexed relation. */ matching_cols = 1; - largs_cell = lnext(list_head(clause->largs)); - rargs_cell = lnext(list_head(clause->rargs)); + vargs_cell = lnext(list_head(var_args)); + nargs_cell = lnext(list_head(non_var_args)); opnos_cell = lnext(list_head(clause->opnos)); collids_cell = lnext(list_head(clause->inputcollids)); - while (largs_cell != NULL) + while (vargs_cell != NULL) { - Node *varop; - Node *constop; + Node *varop = (Node *) lfirst(vargs_cell); + Node *constop = (Node *) lfirst(nargs_cell); int i; expr_op = lfirst_oid(opnos_cell); - if (var_on_left) + if (!var_on_left) { - varop = (Node *) lfirst(largs_cell); - constop = (Node *) lfirst(rargs_cell); - } - else - { - varop = (Node *) lfirst(rargs_cell); - constop = (Node *) lfirst(largs_cell); /* indexkey is on right, so commute the operator */ expr_op = get_commutator(expr_op); if (expr_op == InvalidOid) @@ -3980,37 +3997,49 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, /* Add column number to returned list */ *indexcolnos = lappend_int(*indexcolnos, i); - /* Add opfamily and datatypes to lists */ + /* Add operator info to lists */ get_op_opfamily_properties(expr_op, index->opfamily[i], false, &op_strategy, &op_lefttype, &op_righttype); + expr_ops = lappend_oid(expr_ops, expr_op); opfamilies = lappend_oid(opfamilies, index->opfamily[i]); lefttypes = lappend_oid(lefttypes, op_lefttype); righttypes = lappend_oid(righttypes, op_righttype); /* This column matches, keep scanning */ matching_cols++; - largs_cell = lnext(largs_cell); - rargs_cell = lnext(rargs_cell); + vargs_cell = lnext(vargs_cell); + nargs_cell = lnext(nargs_cell); opnos_cell = lnext(opnos_cell); collids_cell = lnext(collids_cell); } - /* Return clause as-is if it's all usable as index quals */ - if (matching_cols == list_length(clause->opnos)) - return (Expr *) clause; + /* Result is non-lossy if all columns are usable as index quals */ + *lossy = (matching_cols != list_length(clause->opnos)); /* - * We have to generate a subset rowcompare (possibly just one OpExpr). The - * painful part of this is changing < to <= or > to >=, so deal with that - * first. + * Return clause as-is if we have var on left and it's all usable as index + * quals */ - if (op_strategy == BTLessEqualStrategyNumber || - op_strategy == BTGreaterEqualStrategyNumber) + if (var_on_left && !*lossy) + return rinfo; + + /* + * We have to generate a modified rowcompare (possibly just one OpExpr). + * The painful part of this is changing < to <= or > to >=, so deal with + * that first. + */ + if (!*lossy) { - /* easy, just use the same operators */ - new_ops = list_truncate(list_copy(clause->opnos), matching_cols); + /* very easy, just use the commuted operators */ + new_ops = expr_ops; + } + else if (op_strategy == BTLessEqualStrategyNumber || + op_strategy == BTGreaterEqualStrategyNumber) + { + /* easy, just use the same (possibly commuted) operators */ + new_ops = list_truncate(expr_ops, matching_cols); } else { @@ -4025,9 +4054,9 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, else elog(ERROR, "unexpected strategy number %d", op_strategy); new_ops = NIL; - lefttypes_cell = list_head(lefttypes); - righttypes_cell = list_head(righttypes); - foreach(opfamilies_cell, opfamilies) + forthree(opfamilies_cell, opfamilies, + lefttypes_cell, lefttypes, + righttypes_cell, righttypes) { Oid opfam = lfirst_oid(opfamilies_cell); Oid lefttype = lfirst_oid(lefttypes_cell); @@ -4038,16 +4067,7 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, if (!OidIsValid(expr_op)) /* should not happen */ elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", op_strategy, lefttype, righttype, opfam); - if (!var_on_left) - { - expr_op = get_commutator(expr_op); - if (!OidIsValid(expr_op)) /* should not happen */ - elog(ERROR, "could not find commutator of operator %d(%u,%u) of opfamily %u", - op_strategy, lefttype, righttype, opfam); - } new_ops = lappend_oid(new_ops, expr_op); - lefttypes_cell = lnext(lefttypes_cell); - righttypes_cell = lnext(righttypes_cell); } } @@ -4056,29 +4076,31 @@ adjust_rowcompare_for_index(RowCompareExpr *clause, { RowCompareExpr *rc = makeNode(RowCompareExpr); - if (var_on_left) - rc->rctype = (RowCompareType) op_strategy; - else - rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ? - ROWCOMPARE_GE : ROWCOMPARE_LE; + rc->rctype = (RowCompareType) op_strategy; rc->opnos = new_ops; rc->opfamilies = list_truncate(list_copy(clause->opfamilies), matching_cols); rc->inputcollids = list_truncate(list_copy(clause->inputcollids), matching_cols); - rc->largs = list_truncate(copyObject(clause->largs), + rc->largs = list_truncate(copyObject(var_args), matching_cols); - rc->rargs = list_truncate(copyObject(clause->rargs), + rc->rargs = list_truncate(copyObject(non_var_args), matching_cols); - return (Expr *) rc; + return make_simple_restrictinfo((Expr *) rc); } else { - return make_opclause(linitial_oid(new_ops), BOOLOID, false, - copyObject(linitial(clause->largs)), - copyObject(linitial(clause->rargs)), - InvalidOid, - linitial_oid(clause->inputcollids)); + Expr *op; + + /* We don't report an index column list in this case */ + *indexcolnos = NIL; + + op = make_opclause(linitial_oid(new_ops), BOOLOID, false, + copyObject(linitial(var_args)), + copyObject(linitial(non_var_args)), + InvalidOid, + linitial_oid(clause->inputcollids)); + return make_simple_restrictinfo(op); } } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 1b4f7db649e..c7645acad2c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -152,8 +152,13 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path) static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); -static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); +static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, + List **stripped_indexquals_p, + List **fixed_indexquals_p); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); +static Node *fix_indexqual_clause(PlannerInfo *root, + IndexOptInfo *index, int indexcol, + Node *clause, List *indexcolnos); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); static List *get_switched_clauses(List *clauses, Relids outerrelids); static List *order_qual_clauses(PlannerInfo *root, List *clauses); @@ -2607,7 +2612,7 @@ create_indexscan_plan(PlannerInfo *root, bool indexonly) { Scan *scan_plan; - List *indexquals = best_path->indexquals; + List *indexclauses = best_path->indexclauses; List *indexorderbys = best_path->indexorderbys; Index baserelid = best_path->path.parent->relid; Oid indexoid = best_path->indexinfo->indexoid; @@ -2623,16 +2628,14 @@ create_indexscan_plan(PlannerInfo *root, Assert(best_path->path.parent->rtekind == RTE_RELATION); /* - * Build "stripped" indexquals structure (no RestrictInfos) to pass to - * executor as indexqualorig + * Extract the index qual expressions (stripped of RestrictInfos) from the + * IndexClauses list, and prepare a copy with index Vars substituted for + * table Vars. (This step also does replace_nestloop_params on the + * fixed_indexquals.) */ - stripped_indexquals = get_actual_clauses(indexquals); - - /* - * The executor needs a copy with the indexkey on the left of each clause - * and with index Vars substituted for table ones. - */ - fixed_indexquals = fix_indexqual_references(root, best_path); + fix_indexqual_references(root, best_path, + &stripped_indexquals, + &fixed_indexquals); /* * Likewise fix up index attr references in the ORDER BY expressions. @@ -2648,14 +2651,14 @@ create_indexscan_plan(PlannerInfo *root, * included in qpqual. The upshot is that qpqual must contain * scan_clauses minus whatever appears in indexquals. * - * In normal cases simple pointer equality checks will be enough to spot - * duplicate RestrictInfos, so we try that first. - * - * Another common case is that a scan_clauses entry is generated from the - * same EquivalenceClass as some indexqual, and is therefore redundant - * with it, though not equal. (This happens when indxpath.c prefers a + * is_redundant_with_indexclauses() detects cases where a scan clause is + * present in the indexclauses list or is generated from the same + * EquivalenceClass as some indexclause, and is therefore redundant with + * it, though not equal. (The latter happens when indxpath.c prefers a * different derived equality than what generate_join_implied_equalities - * picked for a parameterized scan's ppi_clauses.) + * picked for a parameterized scan's ppi_clauses.) Note that it will not + * match to lossy index clauses, which is critical because we have to + * include the original clause in qpqual in that case. * * In some situations (particularly with OR'd index conditions) we may * have scan_clauses that are not equal to, but are logically implied by, @@ -2674,12 +2677,11 @@ create_indexscan_plan(PlannerInfo *root, if (rinfo->pseudoconstant) continue; /* we may drop pseudoconstants here */ - if (list_member_ptr(indexquals, rinfo)) - continue; /* simple duplicate */ - if (is_redundant_derived_clause(rinfo, indexquals)) - continue; /* derived from same EquivalenceClass */ + if (is_redundant_with_indexclauses(rinfo, indexclauses)) + continue; /* dup or derived from same EquivalenceClass */ if (!contain_mutable_functions((Node *) rinfo->clause) && - predicate_implied_by(list_make1(rinfo->clause), indexquals, false)) + predicate_implied_by(list_make1(rinfo->clause), stripped_indexquals, + false)) continue; /* provably implied by indexquals */ qpqual = lappend(qpqual, rinfo); } @@ -3040,6 +3042,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, { IndexPath *ipath = (IndexPath *) bitmapqual; IndexScan *iscan; + List *subquals; + List *subindexquals; List *subindexECs; ListCell *l; @@ -3060,8 +3064,26 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, plan->plan_width = 0; /* meaningless */ plan->parallel_aware = false; plan->parallel_safe = ipath->path.parallel_safe; - *qual = get_actual_clauses(ipath->indexclauses); - *indexqual = get_actual_clauses(ipath->indexquals); + /* Extract original index clauses, actual index quals, relevant ECs */ + subquals = NIL; + subindexquals = NIL; + subindexECs = NIL; + foreach(l, ipath->indexclauses) + { + IndexClause *iclause = (IndexClause *) lfirst(l); + RestrictInfo *rinfo = iclause->rinfo; + + Assert(!rinfo->pseudoconstant); + subquals = lappend(subquals, rinfo->clause); + if (iclause->indexquals) + subindexquals = list_concat(subindexquals, + get_actual_clauses(iclause->indexquals)); + else + subindexquals = lappend(subindexquals, rinfo->clause); + if (rinfo->parent_ec) + subindexECs = lappend(subindexECs, rinfo->parent_ec); + } + /* We can add any index predicate conditions, too */ foreach(l, ipath->indexinfo->indpred) { Expr *pred = (Expr *) lfirst(l); @@ -3072,21 +3094,14 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, * the conditions that got pushed into the bitmapqual. Avoid * generating redundant conditions. */ - if (!predicate_implied_by(list_make1(pred), ipath->indexclauses, - false)) + if (!predicate_implied_by(list_make1(pred), subquals, false)) { - *qual = lappend(*qual, pred); - *indexqual = lappend(*indexqual, pred); + subquals = lappend(subquals, pred); + subindexquals = lappend(subindexquals, pred); } } - subindexECs = NIL; - foreach(l, ipath->indexquals) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - - if (rinfo->parent_ec) - subindexECs = lappend(subindexECs, rinfo->parent_ec); - } + *qual = subquals; + *indexqual = subindexquals; *indexECs = subindexECs; } else @@ -4446,138 +4461,67 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) * Adjust indexqual clauses to the form the executor's indexqual * machinery needs. * - * We have four tasks here: - * * Remove RestrictInfo nodes from the input clauses. + * We have three tasks here: + * * Select the actual qual clauses out of the input IndexClause list, + * and remove RestrictInfo nodes from the qual clauses. * * Replace any outer-relation Var or PHV nodes with nestloop Params. * (XXX eventually, that responsibility should go elsewhere?) * * Index keys must be represented by Var nodes with varattno set to the * index's attribute number, not the attribute number in the original rel. - * * If the index key is on the right, commute the clause to put it on the - * left. * - * The result is a modified copy of the path's indexquals list --- the - * original is not changed. Note also that the copy shares no substructure - * with the original; this is needed in case there is a subplan in it (we need - * two separate copies of the subplan tree, or things will go awry). + * *stripped_indexquals_p receives a list of the actual qual clauses. + * + * *fixed_indexquals_p receives a list of the adjusted quals. This is a copy + * that shares no substructure with the original; this is needed in case there + * are subplans in it (we need two separate copies of the subplan tree, or + * things will go awry). */ -static List * -fix_indexqual_references(PlannerInfo *root, IndexPath *index_path) +static void +fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, + List **stripped_indexquals_p, List **fixed_indexquals_p) { IndexOptInfo *index = index_path->indexinfo; + List *stripped_indexquals; List *fixed_indexquals; - ListCell *lcc, - *lci; + ListCell *lc; - fixed_indexquals = NIL; + stripped_indexquals = fixed_indexquals = NIL; - forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols) + foreach(lc, index_path->indexclauses) { - RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc); - int indexcol = lfirst_int(lci); - Node *clause; + IndexClause *iclause = lfirst_node(IndexClause, lc); + int indexcol = iclause->indexcol; - /* - * Replace any outer-relation variables with nestloop params. - * - * This also makes a copy of the clause, so it's safe to modify it - * in-place below. - */ - clause = replace_nestloop_params(root, (Node *) rinfo->clause); - - if (IsA(clause, OpExpr)) + if (iclause->indexquals == NIL) { - OpExpr *op = (OpExpr *) clause; - - if (list_length(op->args) != 2) - elog(ERROR, "indexqual clause is not binary opclause"); - - /* - * Check to see if the indexkey is on the right; if so, commute - * the clause. The indexkey should be the side that refers to - * (only) the base relation. - */ - if (!bms_equal(rinfo->left_relids, index->rel->relids)) - CommuteOpExpr(op); + /* rinfo->clause is directly usable as an indexqual */ + Node *clause = (Node *) iclause->rinfo->clause; - /* - * Now replace the indexkey expression with an index Var. - */ - linitial(op->args) = fix_indexqual_operand(linitial(op->args), - index, - indexcol); + stripped_indexquals = lappend(stripped_indexquals, clause); + clause = fix_indexqual_clause(root, index, indexcol, + clause, iclause->indexcols); + fixed_indexquals = lappend(fixed_indexquals, clause); } - else if (IsA(clause, RowCompareExpr)) + else { - RowCompareExpr *rc = (RowCompareExpr *) clause; - Expr *newrc; - List *indexcolnos; - bool var_on_left; - ListCell *lca, - *lcai; + /* Process the derived indexquals */ + ListCell *lc2; - /* - * Re-discover which index columns are used in the rowcompare. - */ - newrc = adjust_rowcompare_for_index(rc, - index, - indexcol, - &indexcolnos, - &var_on_left); - - /* - * Trouble if adjust_rowcompare_for_index thought the - * RowCompareExpr didn't match the index as-is; the clause should - * have gone through that routine already. - */ - if (newrc != (Expr *) rc) - elog(ERROR, "inconsistent results from adjust_rowcompare_for_index"); - - /* - * Check to see if the indexkey is on the right; if so, commute - * the clause. - */ - if (!var_on_left) - CommuteRowCompareExpr(rc); - - /* - * Now replace the indexkey expressions with index Vars. - */ - Assert(list_length(rc->largs) == list_length(indexcolnos)); - forboth(lca, rc->largs, lcai, indexcolnos) + foreach(lc2, iclause->indexquals) { - lfirst(lca) = fix_indexqual_operand(lfirst(lca), - index, - lfirst_int(lcai)); - } - } - else if (IsA(clause, ScalarArrayOpExpr)) - { - ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; - - /* Never need to commute... */ + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2); + Node *clause = (Node *) rinfo->clause; - /* Replace the indexkey expression with an index Var. */ - linitial(saop->args) = fix_indexqual_operand(linitial(saop->args), - index, - indexcol); - } - else if (IsA(clause, NullTest)) - { - NullTest *nt = (NullTest *) clause; - - /* Replace the indexkey expression with an index Var. */ - nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg, - index, - indexcol); + stripped_indexquals = lappend(stripped_indexquals, clause); + clause = fix_indexqual_clause(root, index, indexcol, + clause, iclause->indexcols); + fixed_indexquals = lappend(fixed_indexquals, clause); + } } - else - elog(ERROR, "unsupported indexqual type: %d", - (int) nodeTag(clause)); - - fixed_indexquals = lappend(fixed_indexquals, clause); } - return fixed_indexquals; + *stripped_indexquals_p = stripped_indexquals; + *fixed_indexquals_p = fixed_indexquals; } /* @@ -4585,11 +4529,8 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path) * Adjust indexorderby clauses to the form the executor's index * machinery needs. * - * This is a simplified version of fix_indexqual_references. The input does - * not have RestrictInfo nodes, and we assume that indxpath.c already - * commuted the clauses to put the index keys on the left. Also, we don't - * bother to support any cases except simple OpExprs, since nothing else - * is allowed for ordering operators. + * This is a simplified version of fix_indexqual_references. The input is + * bare clauses and a separate indexcol list, instead of IndexClauses. */ static List * fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path) @@ -4606,36 +4547,79 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path) Node *clause = (Node *) lfirst(lcc); int indexcol = lfirst_int(lci); - /* - * Replace any outer-relation variables with nestloop params. - * - * This also makes a copy of the clause, so it's safe to modify it - * in-place below. - */ - clause = replace_nestloop_params(root, clause); + clause = fix_indexqual_clause(root, index, indexcol, clause, NIL); + fixed_indexorderbys = lappend(fixed_indexorderbys, clause); + } - if (IsA(clause, OpExpr)) - { - OpExpr *op = (OpExpr *) clause; + return fixed_indexorderbys; +} - if (list_length(op->args) != 2) - elog(ERROR, "indexorderby clause is not binary opclause"); +/* + * fix_indexqual_clause + * Convert a single indexqual clause to the form needed by the executor. + * + * We replace nestloop params here, and replace the index key variables + * or expressions by index Var nodes. + */ +static Node * +fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol, + Node *clause, List *indexcolnos) +{ + /* + * Replace any outer-relation variables with nestloop params. + * + * This also makes a copy of the clause, so it's safe to modify it + * in-place below. + */ + clause = replace_nestloop_params(root, clause); - /* - * Now replace the indexkey expression with an index Var. - */ - linitial(op->args) = fix_indexqual_operand(linitial(op->args), - index, - indexcol); + if (IsA(clause, OpExpr)) + { + OpExpr *op = (OpExpr *) clause; + + /* Replace the indexkey expression with an index Var. */ + linitial(op->args) = fix_indexqual_operand(linitial(op->args), + index, + indexcol); + } + else if (IsA(clause, RowCompareExpr)) + { + RowCompareExpr *rc = (RowCompareExpr *) clause; + ListCell *lca, + *lcai; + + /* Replace the indexkey expressions with index Vars. */ + Assert(list_length(rc->largs) == list_length(indexcolnos)); + forboth(lca, rc->largs, lcai, indexcolnos) + { + lfirst(lca) = fix_indexqual_operand(lfirst(lca), + index, + lfirst_int(lcai)); } - else - elog(ERROR, "unsupported indexorderby type: %d", - (int) nodeTag(clause)); + } + else if (IsA(clause, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; - fixed_indexorderbys = lappend(fixed_indexorderbys, clause); + /* Replace the indexkey expression with an index Var. */ + linitial(saop->args) = fix_indexqual_operand(linitial(saop->args), + index, + indexcol); } + else if (IsA(clause, NullTest)) + { + NullTest *nt = (NullTest *) clause; - return fixed_indexorderbys; + /* Replace the indexkey expression with an index Var. */ + nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg, + index, + indexcol); + } + else + elog(ERROR, "unsupported indexqual type: %d", + (int) nodeTag(clause)); + + return clause; } /* diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b2239728cf9..ddb86bd0c30 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -6136,7 +6136,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) /* Estimate the cost of index scan */ indexScanPath = create_index_path(root, indexInfo, - NIL, NIL, NIL, NIL, NIL, + NIL, NIL, NIL, NIL, ForwardScanDirection, false, NULL, 1.0, false); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 663fa7cd339..d7ff17c363d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -2157,71 +2157,6 @@ CommuteOpExpr(OpExpr *clause) } /* - * CommuteRowCompareExpr: commute a RowCompareExpr clause - * - * XXX the clause is destructively modified! - */ -void -CommuteRowCompareExpr(RowCompareExpr *clause) -{ - List *newops; - List *temp; - ListCell *l; - - /* Sanity checks: caller is at fault if these fail */ - if (!IsA(clause, RowCompareExpr)) - elog(ERROR, "expected a RowCompareExpr"); - - /* Build list of commuted operators */ - newops = NIL; - foreach(l, clause->opnos) - { - Oid opoid = lfirst_oid(l); - - opoid = get_commutator(opoid); - if (!OidIsValid(opoid)) - elog(ERROR, "could not find commutator for operator %u", - lfirst_oid(l)); - newops = lappend_oid(newops, opoid); - } - - /* - * modify the clause in-place! - */ - switch (clause->rctype) - { - case ROWCOMPARE_LT: - clause->rctype = ROWCOMPARE_GT; - break; - case ROWCOMPARE_LE: - clause->rctype = ROWCOMPARE_GE; - break; - case ROWCOMPARE_GE: - clause->rctype = ROWCOMPARE_LE; - break; - case ROWCOMPARE_GT: - clause->rctype = ROWCOMPARE_LT; - break; - default: - elog(ERROR, "unexpected RowCompare type: %d", - (int) clause->rctype); - break; - } - - clause->opnos = newops; - - /* - * Note: we need not change the opfamilies list; we assume any btree - * opfamily containing an operator will also contain its commutator. - * Collations don't change either. - */ - - temp = clause->largs; - clause->largs = clause->rargs; - clause->rargs = temp; -} - -/* * Helper for eval_const_expressions: check that datatype of an attribute * is still what it was when the expression was parsed. This is needed to * guard against improper simplification after ALTER COLUMN TYPE. (XXX we diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 08133a28fd2..a3e64110d36 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1001,10 +1001,8 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer * Creates a path node for an index scan. * * 'index' is a usable index. - * 'indexclauses' is a list of RestrictInfo nodes representing clauses - * to be used as index qual conditions in the scan. - * 'indexclausecols' is an integer list of index column numbers (zero based) - * the indexclauses can be used with. + * 'indexclauses' is a list of IndexClause nodes representing clauses + * to be enforced as qual conditions in the scan. * 'indexorderbys' is a list of bare expressions (no RestrictInfos) * to be used as index ordering operators in the scan. * 'indexorderbycols' is an integer list of index column numbers (zero based) @@ -1025,7 +1023,6 @@ IndexPath * create_index_path(PlannerInfo *root, IndexOptInfo *index, List *indexclauses, - List *indexclausecols, List *indexorderbys, List *indexorderbycols, List *pathkeys, @@ -1037,8 +1034,6 @@ create_index_path(PlannerInfo *root, { IndexPath *pathnode = makeNode(IndexPath); RelOptInfo *rel = index->rel; - List *indexquals, - *indexqualcols; pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan; pathnode->path.parent = rel; @@ -1050,15 +1045,8 @@ create_index_path(PlannerInfo *root, pathnode->path.parallel_workers = 0; pathnode->path.pathkeys = pathkeys; - /* Convert clauses to indexquals the executor can handle */ - expand_indexqual_conditions(index, indexclauses, indexclausecols, - &indexquals, &indexqualcols); - - /* Fill in the pathnode */ pathnode->indexinfo = index; pathnode->indexclauses = indexclauses; - pathnode->indexquals = indexquals; - pathnode->indexqualcols = indexqualcols; pathnode->indexorderbys = indexorderbys; pathnode->indexorderbycols = indexorderbycols; pathnode->indexscandir = indexscandir; @@ -3809,7 +3797,6 @@ do { \ FLAT_COPY_PATH(ipath, path, IndexPath); ADJUST_CHILD_ATTRS(ipath->indexclauses); - ADJUST_CHILD_ATTRS(ipath->indexquals); new_path = (Path *) ipath; } break; diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 1c47c708028..03e5f12d0da 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -289,6 +289,70 @@ make_sub_restrictinfos(Expr *clause, } /* + * commute_restrictinfo + * + * Given a RestrictInfo containing a binary opclause, produce a RestrictInfo + * representing the commutation of that clause. The caller must pass the + * OID of the commutator operator (which it's presumably looked up, else + * it would not know this is valid). + * + * Beware that the result shares sub-structure with the given RestrictInfo. + * That's okay for the intended usage with derived index quals, but might + * be hazardous if the source is subject to change. Also notice that we + * assume without checking that the commutator op is a member of the same + * btree and hash opclasses as the original op. + */ +RestrictInfo * +commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op) +{ + RestrictInfo *result; + OpExpr *newclause; + OpExpr *clause = castNode(OpExpr, rinfo->clause); + + Assert(list_length(clause->args) == 2); + + /* flat-copy all the fields of clause ... */ + newclause = makeNode(OpExpr); + memcpy(newclause, clause, sizeof(OpExpr)); + + /* ... and adjust those we need to change to commute it */ + newclause->opno = comm_op; + newclause->opfuncid = InvalidOid; + newclause->args = list_make2(lsecond(clause->args), + linitial(clause->args)); + + /* likewise, flat-copy all the fields of rinfo ... */ + result = makeNode(RestrictInfo); + memcpy(result, rinfo, sizeof(RestrictInfo)); + + /* + * ... and adjust those we need to change. Note in particular that we can + * preserve any cached selectivity or cost estimates, since those ought to + * be the same for the new clause. Likewise we can keep the source's + * parent_ec. + */ + result->clause = (Expr *) newclause; + result->left_relids = rinfo->right_relids; + result->right_relids = rinfo->left_relids; + Assert(result->orclause == NULL); + result->left_ec = rinfo->right_ec; + result->right_ec = rinfo->left_ec; + result->left_em = rinfo->right_em; + result->right_em = rinfo->left_em; + result->scansel_cache = NIL; /* not worth updating this */ + if (rinfo->hashjoinoperator == clause->opno) + result->hashjoinoperator = comm_op; + else + result->hashjoinoperator = InvalidOid; + result->left_bucketsize = rinfo->right_bucketsize; + result->right_bucketsize = rinfo->left_bucketsize; + result->left_mcvfreq = rinfo->right_mcvfreq; + result->right_mcvfreq = rinfo->left_mcvfreq; + + return result; +} + +/* * restriction_is_or_clause * * Returns t iff the restrictinfo node contains an 'or' clause. diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fb005046766..74fafc64f3e 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -226,6 +226,8 @@ static Selectivity regex_selectivity(const char *patt, int pattlen, static Datum string_to_datum(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype); static Const *string_to_bytea_const(const char *str, size_t str_len); +static IndexQualInfo *deconstruct_indexqual(RestrictInfo *rinfo, + IndexOptInfo *index, int indexcol); static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals); @@ -6574,21 +6576,72 @@ string_to_bytea_const(const char *str, size_t str_len) *------------------------------------------------------------------------- */ +/* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */ +static List * +get_index_quals(List *indexclauses) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, indexclauses) + { + IndexClause *iclause = lfirst_node(IndexClause, lc); + + if (iclause->indexquals == NIL) + { + /* rinfo->clause is directly usable as an indexqual */ + result = lappend(result, iclause->rinfo); + } + else + { + /* report the derived indexquals */ + result = list_concat(result, list_copy(iclause->indexquals)); + } + } + return result; +} + List * deconstruct_indexquals(IndexPath *path) { List *result = NIL; IndexOptInfo *index = path->indexinfo; - ListCell *lcc, - *lci; + ListCell *lc; - forboth(lcc, path->indexquals, lci, path->indexqualcols) + foreach(lc, path->indexclauses) + { + IndexClause *iclause = lfirst_node(IndexClause, lc); + int indexcol = iclause->indexcol; + IndexQualInfo *qinfo; + + if (iclause->indexquals == NIL) + { + /* rinfo->clause is directly usable as an indexqual */ + qinfo = deconstruct_indexqual(iclause->rinfo, index, indexcol); + result = lappend(result, qinfo); + } + else + { + /* Process the derived indexquals */ + ListCell *lc2; + + foreach(lc2, iclause->indexquals) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2); + + qinfo = deconstruct_indexqual(rinfo, index, indexcol); + result = lappend(result, qinfo); + } + } + } + return result; +} + +static IndexQualInfo * +deconstruct_indexqual(RestrictInfo *rinfo, IndexOptInfo *index, int indexcol) +{ { - RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc); - int indexcol = lfirst_int(lci); Expr *clause; - Node *leftop, - *rightop; IndexQualInfo *qinfo; clause = rinfo->clause; @@ -6600,57 +6653,25 @@ deconstruct_indexquals(IndexPath *path) if (IsA(clause, OpExpr)) { qinfo->clause_op = ((OpExpr *) clause)->opno; - leftop = get_leftop(clause); - rightop = get_rightop(clause); - if (match_index_to_operand(leftop, indexcol, index)) - { - qinfo->varonleft = true; - qinfo->other_operand = rightop; - } - else - { - Assert(match_index_to_operand(rightop, indexcol, index)); - qinfo->varonleft = false; - qinfo->other_operand = leftop; - } + qinfo->other_operand = get_rightop(clause); } else if (IsA(clause, RowCompareExpr)) { RowCompareExpr *rc = (RowCompareExpr *) clause; qinfo->clause_op = linitial_oid(rc->opnos); - /* Examine only first columns to determine left/right sides */ - if (match_index_to_operand((Node *) linitial(rc->largs), - indexcol, index)) - { - qinfo->varonleft = true; - qinfo->other_operand = (Node *) rc->rargs; - } - else - { - Assert(match_index_to_operand((Node *) linitial(rc->rargs), - indexcol, index)); - qinfo->varonleft = false; - qinfo->other_operand = (Node *) rc->largs; - } + qinfo->other_operand = (Node *) rc->rargs; } else if (IsA(clause, ScalarArrayOpExpr)) { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; qinfo->clause_op = saop->opno; - /* index column is always on the left in this case */ - Assert(match_index_to_operand((Node *) linitial(saop->args), - indexcol, index)); - qinfo->varonleft = true; qinfo->other_operand = (Node *) lsecond(saop->args); } else if (IsA(clause, NullTest)) { qinfo->clause_op = InvalidOid; - Assert(match_index_to_operand((Node *) ((NullTest *) clause)->arg, - indexcol, index)); - qinfo->varonleft = true; qinfo->other_operand = NULL; } else @@ -6659,9 +6680,8 @@ deconstruct_indexquals(IndexPath *path) (int) nodeTag(clause)); } - result = lappend(result, qinfo); + return qinfo; } - return result; } /* @@ -6731,7 +6751,7 @@ genericcostestimate(PlannerInfo *root, GenericCosts *costs) { IndexOptInfo *index = path->indexinfo; - List *indexQuals = path->indexquals; + List *indexQuals = get_index_quals(path->indexclauses); List *indexOrderBys = path->indexorderbys; Cost indexStartupCost; Cost indexTotalCost; @@ -7052,14 +7072,8 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, } } - /* - * We would need to commute the clause_op if not varonleft, except - * that we only care if it's equality or not, so that refinement is - * unnecessary. - */ - clause_op = qinfo->clause_op; - /* check for equality operator */ + clause_op = qinfo->clause_op; if (OidIsValid(clause_op)) { op_strategy = get_op_opfamily_strategy(clause_op, @@ -7560,12 +7574,6 @@ gincost_opexpr(PlannerInfo *root, Oid clause_op = qinfo->clause_op; Node *operand = qinfo->other_operand; - if (!qinfo->varonleft) - { - /* must commute the operator */ - clause_op = get_commutator(clause_op); - } - /* aggressively reduce to a constant, and look through relabeling */ operand = estimate_expression_value(root, operand); @@ -7728,7 +7736,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double *indexPages) { IndexOptInfo *index = path->indexinfo; - List *indexQuals = path->indexquals; + List *indexQuals = get_index_quals(path->indexclauses); List *indexOrderBys = path->indexorderbys; List *qinfos; ListCell *l; @@ -7831,26 +7839,11 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, numEntries = 1; /* - * Include predicate in selectivityQuals (should match - * genericcostestimate) + * If the index is partial, AND the index predicate with the index-bound + * quals to produce a more accurate idea of the number of rows covered by + * the bound conditions. */ - if (index->indpred != NIL) - { - List *predExtraQuals = NIL; - - foreach(l, index->indpred) - { - Node *predQual = (Node *) lfirst(l); - List *oneQual = list_make1(predQual); - - if (!predicate_implied_by(oneQual, indexQuals, false)) - predExtraQuals = list_concat(predExtraQuals, oneQual); - } - /* list_concat avoids modifying the passed-in indexQuals list */ - selectivityQuals = list_concat(predExtraQuals, indexQuals); - } - else - selectivityQuals = indexQuals; + selectivityQuals = add_predicate_to_quals(index, indexQuals); /* Estimate the fraction of main-table tuples that will be visited */ *indexSelectivity = clauselist_selectivity(root, selectivityQuals, @@ -8053,7 +8046,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, double *indexPages) { IndexOptInfo *index = path->indexinfo; - List *indexQuals = path->indexquals; + List *indexQuals = get_index_quals(path->indexclauses); double numPages = index->pages; RelOptInfo *baserel = index->rel; RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); |