diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 50 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 399 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 303 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 35 |
5 files changed, 342 insertions, 447 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 7bc1e6e2ba1..efc49dad1ec 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -52,7 +52,9 @@ * results into. All the input data they need is passed as separate * parameters, even though much of it could be extracted from the Path. * An exception is made for the cost_XXXjoin() routines, which expect all - * the non-cost fields of the passed XXXPath to be filled in. + * the non-cost fields of the passed XXXPath to be filled in, and similarly + * cost_index() assumes the passed IndexPath is valid except for its output + * values. * * * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group @@ -208,38 +210,28 @@ cost_seqscan(Path *path, PlannerInfo *root, * cost_index * Determines and returns the cost of scanning a relation using an index. * - * 'index' is the index to be used - * 'indexQuals' is a list of lists of applicable qual clauses (implicit AND - * semantics, one sub-list per index column) - * 'indexOrderBys' is a list of lists of lists of ORDER BY expressions for - * amcanorderbyop indexes (lists per pathkey and index column) - * 'indexonly' is true if it's an index-only scan + * 'path' describes the indexscan under consideration, and is complete + * except for the fields to be set by this routine * '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 + * 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); * NULL for a plain index scan * - * cost_index() takes an IndexPath not just a Path, because it sets a few - * additional fields of the IndexPath besides startup_cost and total_cost. - * These fields are needed if the IndexPath is used in a BitmapIndexScan. + * In addition to startup_cost and total_cost, cost_index() sets the path's + * indextotalcost and indexselectivity fields. These values are needed if + * the IndexPath is used in a BitmapIndexScan. * - * indexQuals is a list of lists of RestrictInfo nodes, but indexOrderBys - * is a list of lists of lists of bare expressions. - * - * NOTE: 'indexQuals' must contain only clauses usable as index restrictions. - * Any additional quals evaluated as qpquals may reduce the number of returned - * tuples, but they won't reduce the number of tuples we have to fetch from - * the table, so they don't reduce the scan cost. + * NOTE: path->indexquals must contain only clauses usable as index + * restrictions. Any additional quals evaluated as qpquals may reduce the + * number of returned tuples, but they won't reduce the number of tuples + * we have to fetch from the table, so they don't reduce the scan cost. */ void -cost_index(IndexPath *path, PlannerInfo *root, - IndexOptInfo *index, - List *indexQuals, - List *indexOrderBys, - bool indexonly, - RelOptInfo *outer_rel) +cost_index(IndexPath *path, PlannerInfo *root, RelOptInfo *outer_rel) { + IndexOptInfo *index = path->indexinfo; RelOptInfo *baserel = index->rel; + bool indexonly = (path->path.pathtype == T_IndexOnlyScan); Cost startup_cost = 0; Cost run_cost = 0; Cost indexStartupCost; @@ -271,11 +263,9 @@ cost_index(IndexPath *path, PlannerInfo *root, * the fraction of main-table tuples we will have to retrieve) and its * correlation to the main-table tuple order. */ - OidFunctionCall9(index->amcostestimate, + OidFunctionCall7(index->amcostestimate, PointerGetDatum(root), - PointerGetDatum(index), - PointerGetDatum(indexQuals), - PointerGetDatum(indexOrderBys), + PointerGetDatum(path), PointerGetDatum(outer_rel), PointerGetDatum(&indexStartupCost), PointerGetDatum(&indexTotalCost), @@ -431,7 +421,7 @@ cost_index(IndexPath *path, PlannerInfo *root, { QualCost index_qual_cost; - cost_qual_eval(&index_qual_cost, indexQuals, root); + cost_qual_eval(&index_qual_cost, path->indexquals, root); /* any startup cost still has to be paid ... */ cpu_per_tuple -= index_qual_cost.per_tuple; } @@ -589,7 +579,7 @@ get_indexpath_pages(Path *bitmapqual) * 'baserel' is the relation to be scanned * 'bitmapqual' is a tree of IndexPaths, BitmapAndPaths, and BitmapOrPaths * 'outer_rel' is the outer relation when we are considering using the bitmap - * scan as the inside of a nestloop join (hence, some of the indexQuals + * scan as the inside of a nestloop join (hence, some of the indexquals * are join clauses, and we should expect repeated scans of the table); * NULL for a plain bitmap scan * diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 26d7c1c2331..b851af06a17 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -90,11 +90,13 @@ static PathClauseUsage *classify_index_clause_usage(Path *path, 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, - SaOpControl saop_control, - bool *found_clause); +static void match_clauses_to_index(IndexOptInfo *index, + List *clauses, List *outer_clauses, + Relids outer_relids, + SaOpControl saop_control, + List **index_clauses_p, + List **clause_columns_p, + bool *found_clause); static bool match_clause_to_indexcol(IndexOptInfo *index, int indexcol, RestrictInfo *rinfo, @@ -108,7 +110,9 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index, Oid idxcollation, RowCompareExpr *clause, Relids outer_relids); -static List *match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys); +static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, + List **orderby_clauses_p, + List **clause_columns_p); static Expr *match_clause_to_ordering_op(IndexOptInfo *index, int indexcol, Expr *clause, Oid pk_opfamily); static Relids indexable_outerrelids(PlannerInfo *root, RelOptInfo *rel); @@ -312,7 +316,9 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); IndexPath *ipath; List *restrictclauses; + List *restrictclausecols; List *orderbyclauses; + List *orderbyclausecols; List *index_pathkeys; List *useful_pathkeys; bool useful_predicate; @@ -396,12 +402,14 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, * clauses was used (and, if saop_control is SAOP_REQUIRE, it has to * have been a ScalarArrayOpExpr clause). */ - restrictclauses = group_clauses_by_indexkey(index, - clauses, - outer_clauses, - outer_relids, - saop_control, - &found_clause); + match_clauses_to_index(index, + clauses, + outer_clauses, + outer_relids, + saop_control, + &restrictclauses, + &restrictclausecols, + &found_clause); /* * Not all index AMs support scans with no restriction clauses. We @@ -425,13 +433,15 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, useful_pathkeys = truncate_useless_pathkeys(root, rel, index_pathkeys); orderbyclauses = NIL; + orderbyclausecols = NIL; } else if (index->amcanorderbyop && possibly_useful_pathkeys && istoplevel && outer_rel == NULL && scantype != ST_BITMAPSCAN) { /* see if we can generate ordering operators for query_pathkeys */ - orderbyclauses = match_index_to_pathkeys(index, - root->query_pathkeys); + match_pathkeys_to_index(index, root->query_pathkeys, + &orderbyclauses, + &orderbyclausecols); if (orderbyclauses) useful_pathkeys = root->query_pathkeys; else @@ -441,6 +451,7 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, { useful_pathkeys = NIL; orderbyclauses = NIL; + orderbyclausecols = NIL; } /* @@ -460,7 +471,9 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, restrictclauses, + restrictclausecols, orderbyclauses, + orderbyclausecols, useful_pathkeys, index_is_ordered ? ForwardScanDirection : @@ -485,6 +498,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, { ipath = create_index_path(root, index, restrictclauses, + restrictclausecols, + NIL, NIL, useful_pathkeys, BackwardScanDirection, @@ -1142,14 +1157,15 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) /* - * group_clauses_by_indexkey + * match_clauses_to_index * Find restriction clauses that can be used with an index. * - * Returns a list of sublists of RestrictInfo nodes for clauses that can be - * used with this index. Each sublist contains clauses that can be used - * with one index key (in no particular order); the top list is ordered by - * index key. (This is depended on by expand_indexqual_conditions() and - * fix_indexqual_references().) + * Returns a list of RestrictInfo nodes for clauses that can be used with + * this index, along with an integer list of the index column numbers + * (zero based) that each clause would 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.) + * NIL lists are returned if there are no matching clauses. * * We can use clauses from either the current clauses or outer_clauses lists, * but *found_clause is set TRUE only if we used at least one clause from @@ -1164,40 +1180,38 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) * * If the index has amoptionalkey = false, we give up and return NIL when * there are no restriction clauses matching the first index key. Otherwise, - * we return NIL if there are no restriction clauses matching any index key. - * A non-NIL result will have one (possibly empty) sublist for each index key. - * - * Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4)) - * if we find that clauses C1 and C2 use column A, clauses C3 and C4 use - * column C, and no clauses use column B. + * we return NIL only if there are no restriction clauses matching any index + * key. There could be unused index keys after the first one in any case. * * Note: in some circumstances we may find the same RestrictInfos coming - * from multiple places. Defend against redundant outputs by keeping a side - * list of already-used clauses (pointer equality should be a good enough - * check for this). That also keeps us from matching the same clause to + * from multiple places. Defend against redundant outputs by refusing to + * match an already-used clause (pointer equality should be a good enough + * check for this). This also keeps us from matching the same clause to * multiple columns of a badly-defined index, which is unlikely to be helpful * and is likely to give us an inflated idea of the index's selectivity. */ -static List * -group_clauses_by_indexkey(IndexOptInfo *index, - List *clauses, List *outer_clauses, - Relids outer_relids, - SaOpControl saop_control, - bool *found_clause) +static void +match_clauses_to_index(IndexOptInfo *index, + List *clauses, List *outer_clauses, + Relids outer_relids, + SaOpControl saop_control, + List **index_clauses_p, + List **clause_columns_p, + bool *found_clause) { - List *clausegroup_list = NIL; - List *used_clauses = NIL; - bool found_outer_clause = false; + List *index_clauses = NIL; + List *clause_columns = NIL; int indexcol; - *found_clause = false; /* default result */ + *index_clauses_p = NIL; /* set default results */ + *clause_columns_p = NIL; + *found_clause = false; if (clauses == NIL && outer_clauses == NIL) - return NIL; /* cannot succeed */ + return; /* cannot succeed */ for (indexcol = 0; indexcol < index->ncolumns; indexcol++) { - List *clausegroup = NIL; ListCell *l; /* check the current clauses */ @@ -1206,7 +1220,7 @@ group_clauses_by_indexkey(IndexOptInfo *index, RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); Assert(IsA(rinfo, RestrictInfo)); - if (list_member_ptr(used_clauses, rinfo)) + if (list_member_ptr(index_clauses, rinfo)) continue; if (match_clause_to_indexcol(index, indexcol, @@ -1214,8 +1228,8 @@ group_clauses_by_indexkey(IndexOptInfo *index, outer_relids, saop_control)) { - clausegroup = lappend(clausegroup, rinfo); - used_clauses = lappend(used_clauses, rinfo); + index_clauses = lappend(index_clauses, rinfo); + clause_columns = lappend_int(clause_columns, indexcol); if (saop_control != SAOP_REQUIRE || IsA(rinfo->clause, ScalarArrayOpExpr)) *found_clause = true; @@ -1228,7 +1242,7 @@ group_clauses_by_indexkey(IndexOptInfo *index, RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); Assert(IsA(rinfo, RestrictInfo)); - if (list_member_ptr(used_clauses, rinfo)) + if (list_member_ptr(index_clauses, rinfo)) continue; if (match_clause_to_indexcol(index, indexcol, @@ -1236,27 +1250,20 @@ group_clauses_by_indexkey(IndexOptInfo *index, outer_relids, saop_control)) { - clausegroup = lappend(clausegroup, rinfo); - used_clauses = lappend(used_clauses, rinfo); - found_outer_clause = true; + index_clauses = lappend(index_clauses, rinfo); + clause_columns = lappend_int(clause_columns, indexcol); } } /* * If no clauses match this key, check for amoptionalkey restriction. */ - if (clausegroup == NIL && !index->amoptionalkey && indexcol == 0) - return NIL; - - clausegroup_list = lappend(clausegroup_list, clausegroup); + if (index_clauses == NIL && !index->amoptionalkey) + return; } - list_free(used_clauses); - - if (!*found_clause && !found_outer_clause) - return NIL; /* no indexable clauses anywhere */ - - return clausegroup_list; + *index_clauses_p = index_clauses; + *clause_columns_p = clause_columns; } @@ -1562,24 +1569,33 @@ match_rowcompare_to_indexcol(IndexOptInfo *index, ****************************************************************************/ /* - * match_index_to_pathkeys + * match_pathkeys_to_index * Test whether an index can produce output ordered according to the * given pathkeys using "ordering operators". * - * If it can, return a list of lists of lists of ORDER BY expressions, - * each of the form "indexedcol operator pseudoconstant". If not, return NIL. - * (The top list corresponds to pathkeys and the sub-lists to index columns; - * see comments for indexorderbys in nodes/relation.h.) + * If it can, return a list of suitable ORDER BY expressions, each of the form + * "indexedcol operator pseudoconstant", along with an integer list of the + * index column numbers (zero based) that each clause would be used with. + * NIL lists are returned if the ordering is not achievable this way. + * + * On success, the result list is ordered by pathkeys, and in fact is + * one-to-one with the requested pathkeys. */ -static List * -match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys) +static void +match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, + List **orderby_clauses_p, + List **clause_columns_p) { - List *orderbylists = NIL; + List *orderby_clauses = NIL; + List *clause_columns = NIL; ListCell *lc1; + *orderby_clauses_p = NIL; /* set default results */ + *clause_columns_p = NIL; + /* Only indexes with the amcanorderbyop property are interesting here */ if (!index->amcanorderbyop) - return NIL; + return; foreach(lc1, pathkeys) { @@ -1595,11 +1611,11 @@ match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys) /* Pathkey must request default sort order for the target opfamily */ if (pathkey->pk_strategy != BTLessStrategyNumber || pathkey->pk_nulls_first) - return NIL; + return; /* If eclass is volatile, no hope of using an indexscan */ if (pathkey->pk_eclass->ec_has_volatile) - return NIL; + return; /* Try to match eclass member expression(s) to index */ foreach(lc2, pathkey->pk_eclass->ec_members) @@ -1621,17 +1637,8 @@ match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys) pathkey->pk_opfamily); if (expr) { - /* - * Generate list-of-sublists representation to show which - * index column this expression matches. - */ - List *sublist = NIL; - int i; - - for (i = 0; i < indexcol; i++) - sublist = lappend(sublist, NIL); - sublist = lappend(sublist, list_make1(expr)); - orderbylists = lappend(orderbylists, sublist); + orderby_clauses = lappend(orderby_clauses, expr); + clause_columns = lappend_int(clause_columns, indexcol); found = true; break; } @@ -1642,10 +1649,11 @@ match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys) } if (!found) /* fail if no match for this pathkey */ - return NIL; + return; } - return orderbylists; /* success! */ + *orderby_clauses_p = orderby_clauses; /* success! */ + *clause_columns_p = clause_columns; } /* @@ -2451,65 +2459,6 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel, /**************************************************************************** - * ---- PATH CREATION UTILITIES ---- - ****************************************************************************/ - -/* - * flatten_clausegroups_list - * Given a list of lists of RestrictInfos, flatten it to a list - * of RestrictInfos. - * - * This is used to flatten out a list of sublists of index clauses (such as - * the result of group_clauses_by_indexkey()) into a single list, for use - * where we don't care which clause goes with which index column. The input - * list structure mustn't be altered, but it's OK to share copies of the - * underlying RestrictInfos. - */ -List * -flatten_clausegroups_list(List *clausegroups) -{ - List *allclauses = NIL; - ListCell *l; - - foreach(l, clausegroups) - allclauses = list_concat(allclauses, list_copy((List *) lfirst(l))); - return allclauses; -} - -/* - * flatten_indexorderbys_list - * Given a list of lists of lists of ORDER BY expressions, flatten it. - * - * This is similar to flatten_clausegroups_list, but is used to flatten the - * three-list-level result of match_index_to_pathkeys(). We assume the - * bottom lists each have zero or one member. - */ -List * -flatten_indexorderbys_list(List *indexorderbys) -{ - List *allclauses = NIL; - ListCell *lc1; - - foreach(lc1, indexorderbys) - { - List *sublist = (List *) lfirst(lc1); - ListCell *lc2; - - foreach(lc2, sublist) - { - List *subsublist = (List *) lfirst(lc2); - - if (subsublist == NIL) - continue; - Assert(list_length(subsublist) == 1); - allclauses = lappend(allclauses, (Expr *) linitial(subsublist)); - } - } - return allclauses; -} - - -/**************************************************************************** * ---- ROUTINES TO CHECK OPERANDS ---- ****************************************************************************/ @@ -2635,13 +2584,12 @@ 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 lists of RestrictInfo - * nodes (with implicit AND semantics across list elements) into a list of - * lists of 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 converted into one or more "regular" - * indexqual conditions. + * expand_indexqual_conditions() converts a list of RestrictInfo nodes + * (with implicit AND semantics across list elements) into a list of 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 + * converted into one or more "regular" indexqual conditions. *---------- */ @@ -2855,99 +2803,100 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation, /* * expand_indexqual_conditions - * Given a list of sublists of RestrictInfo nodes, produce a list of lists - * of index qual clauses. 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. - * - * The input clauses are grouped by index key, and so the output is too. + * Given a list of RestrictInfo nodes, produce a list of directly usable + * index qual clauses. + * + * 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.) */ -List * -expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) +void +expand_indexqual_conditions(IndexOptInfo *index, + List *indexclauses, List *indexclausecols, + List **indexquals_p, List **indexqualcols_p) { - List *resultgroups = NIL; - ListCell *lc; - int indexcol; - - if (clausegroups == NIL) - return NIL; - - /* clausegroups must correspond to index columns */ - Assert(list_length(clausegroups) <= index->ncolumns); + List *indexquals = NIL; + List *indexqualcols = NIL; + ListCell *lcc, + *lci; - indexcol = 0; - foreach(lc, clausegroups) + forboth(lcc, indexclauses, lci, indexclausecols) { - List *clausegroup = (List *) lfirst(lc); + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc); + int indexcol = lfirst_int(lci); + Expr *clause = rinfo->clause; Oid curFamily = index->opfamily[indexcol]; Oid curCollation = index->indexcollations[indexcol]; - List *newgroup = NIL; - ListCell *lc2; - foreach(lc2, clausegroup) + /* First check for boolean cases */ + if (IsBooleanOpfamily(curFamily)) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc2); - Expr *clause = rinfo->clause; + Expr *boolqual; - /* First check for boolean cases */ - if (IsBooleanOpfamily(curFamily)) - { - Expr *boolqual; - - boolqual = expand_boolean_index_clause((Node *) clause, - indexcol, - index); - if (boolqual) - { - newgroup = lappend(newgroup, - make_simple_restrictinfo(boolqual)); - continue; - } - } - - /* - * Else it must be an opclause (usual case), ScalarArrayOp, - * RowCompare, or NullTest - */ - if (is_opclause(clause)) - { - newgroup = list_concat(newgroup, - expand_indexqual_opclause(rinfo, - curFamily, - curCollation)); - } - else if (IsA(clause, ScalarArrayOpExpr)) - { - /* no extra work at this time */ - newgroup = lappend(newgroup, rinfo); - } - else if (IsA(clause, RowCompareExpr)) - { - newgroup = lappend(newgroup, - expand_indexqual_rowcompare(rinfo, - index, - indexcol)); - } - else if (IsA(clause, NullTest)) + boolqual = expand_boolean_index_clause((Node *) clause, + indexcol, + index); + if (boolqual) { - Assert(index->amsearchnulls); - newgroup = lappend(newgroup, - make_simple_restrictinfo(clause)); + indexquals = lappend(indexquals, + make_simple_restrictinfo(boolqual)); + indexqualcols = lappend_int(indexqualcols, indexcol); + continue; } - else - elog(ERROR, "unsupported indexqual type: %d", - (int) nodeTag(clause)); } - resultgroups = lappend(resultgroups, newgroup); - - indexcol++; + /* + * Else it must be an opclause (usual case), ScalarArrayOp, + * RowCompare, or NullTest + */ + 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); + } + 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); + } + 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)); } - return resultgroups; + *indexquals_p = indexquals; + *indexqualcols_p = indexqualcols; } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 04024cc4939..cec76e38443 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -83,10 +83,8 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); 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, - List *indexquals); -static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path, - List *indexorderbys); +static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); +static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); 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); @@ -1082,11 +1080,11 @@ create_indexscan_plan(PlannerInfo *root, bool indexonly) { Scan *scan_plan; + List *indexquals = best_path->indexquals; List *indexorderbys = best_path->indexorderbys; Index baserelid = best_path->path.parent->relid; Oid indexoid = best_path->indexinfo->indexoid; List *qpqual; - List *indexquals; List *stripped_indexquals; List *fixed_indexquals; List *fixed_indexorderbys; @@ -1097,13 +1095,6 @@ create_indexscan_plan(PlannerInfo *root, Assert(best_path->path.parent->rtekind == RTE_RELATION); /* - * We need to flatten the indexquals list-of-sublists, since most of the - * processing below doesn't care which index column each qual is - * associated with. - */ - indexquals = flatten_clausegroups_list(best_path->indexquals); - - /* * Build "stripped" indexquals structure (no RestrictInfos) to pass to * executor as indexqualorig */ @@ -1111,23 +1102,14 @@ create_indexscan_plan(PlannerInfo *root, /* * The executor needs a copy with the indexkey on the left of each clause - * and with index Vars substituted for table ones. Here we use the - * unflattened list so we can conveniently tell which index column each - * clause is for. + * and with index Vars substituted for table ones. */ - fixed_indexquals = fix_indexqual_references(root, best_path, - best_path->indexquals); + fixed_indexquals = fix_indexqual_references(root, best_path); /* * Likewise fix up index attr references in the ORDER BY expressions. */ - fixed_indexorderbys = fix_indexorderby_references(root, best_path, - indexorderbys); - - /* - * Also produce a flat list to become the indexorderbyorig. - */ - indexorderbys = flatten_indexorderbys_list(indexorderbys); + fixed_indexorderbys = fix_indexorderby_references(root, best_path); /* * If this is an innerjoin scan, the indexclauses will contain join @@ -1506,7 +1488,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples); plan->plan_width = 0; /* meaningless */ *qual = get_actual_clauses(ipath->indexclauses); - *indexqual = get_actual_clauses(flatten_clausegroups_list(ipath->indexquals)); + *indexqual = get_actual_clauses(ipath->indexquals); foreach(l, ipath->indexinfo->indpred) { Expr *pred = (Expr *) lfirst(l); @@ -2484,8 +2466,7 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) * Adjust indexqual clauses to the form the executor's indexqual * machinery needs. * - * We have five tasks here: - * * Flatten the list-of-sublists structure of indexquals into a simple list. + * We have four tasks here: * * Remove RestrictInfo nodes from the input clauses. * * Replace any outer-relation Var or PHV nodes with nestloop Params. * (XXX eventually, that responsibility should go elsewhere?) @@ -2494,140 +2475,128 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) * * 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 indexquals list --- the + * 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). */ static List * -fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, - List *indexquals) +fix_indexqual_references(PlannerInfo *root, IndexPath *index_path) { IndexOptInfo *index = index_path->indexinfo; List *fixed_indexquals; - ListCell *lc1; - int indexcol; + ListCell *lcc, + *lci; fixed_indexquals = NIL; - /* clausegroups must correspond to index columns */ - Assert(list_length(indexquals) <= index->ncolumns); - - indexcol = 0; - foreach(lc1, indexquals) + forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols) { - List *clausegroup = (List *) lfirst(lc1); - ListCell *lc2; + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc); + int indexcol = lfirst_int(lci); + Node *clause; + + Assert(IsA(rinfo, RestrictInfo)); + + /* + * 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); - foreach(lc2, clausegroup) + if (IsA(clause, OpExpr)) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc2); - Node *clause; + OpExpr *op = (OpExpr *) clause; - Assert(IsA(rinfo, RestrictInfo)); + if (list_length(op->args) != 2) + elog(ERROR, "indexqual clause is not binary opclause"); /* - * 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. + * 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. */ - clause = replace_nestloop_params(root, (Node *) rinfo->clause); + if (!bms_equal(rinfo->left_relids, index->rel->relids)) + CommuteOpExpr(op); - if (IsA(clause, OpExpr)) - { - OpExpr *op = (OpExpr *) clause; + /* + * Now 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; + Expr *newrc; + List *indexcolnos; + bool var_on_left; + ListCell *lca, + *lcai; - if (list_length(op->args) != 2) - elog(ERROR, "indexqual clause is not binary opclause"); + /* + * Re-discover which index columns are used in the rowcompare. + */ + newrc = adjust_rowcompare_for_index(rc, + index, + indexcol, + &indexcolnos, + &var_on_left); - /* - * 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); + /* + * 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"); - /* - * Now 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; - Expr *newrc; - List *indexcolnos; - bool var_on_left; - ListCell *lca, - *lci; + /* + * Check to see if the indexkey is on the right; if so, commute + * the clause. + */ + if (!var_on_left) + CommuteRowCompareExpr(rc); - /* - * Re-discover which index columns are used in the rowcompare. - */ - newrc = adjust_rowcompare_for_index(rc, + /* + * Now 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, - 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, lci, indexcolnos) - { - lfirst(lca) = fix_indexqual_operand(lfirst(lca), - index, - lfirst_int(lci)); - } + lfirst_int(lcai)); } - else if (IsA(clause, ScalarArrayOpExpr)) - { - ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; - - /* Never need to commute... */ + } + else if (IsA(clause, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) 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; + /* Never need to commute... */ - /* Replace the indexkey expression with an index Var. */ - nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg, + /* Replace the indexkey expression with an index Var. */ + linitial(saop->args) = fix_indexqual_operand(linitial(saop->args), index, indexcol); - } - else - elog(ERROR, "unsupported indexqual type: %d", - (int) nodeTag(clause)); + } + else if (IsA(clause, NullTest)) + { + NullTest *nt = (NullTest *) clause; - fixed_indexquals = lappend(fixed_indexquals, clause); + /* 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)); - indexcol++; + fixed_indexquals = lappend(fixed_indexquals, clause); } return fixed_indexquals; @@ -2645,67 +2614,47 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, * is allowed for ordering operators. */ static List * -fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path, - List *indexorderbys) +fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path) { IndexOptInfo *index = index_path->indexinfo; List *fixed_indexorderbys; - ListCell *lc1; + ListCell *lcc, + *lci; fixed_indexorderbys = NIL; - foreach(lc1, indexorderbys) + forboth(lcc, index_path->indexorderbys, lci, index_path->indexorderbycols) { - List *percollists = (List *) lfirst(lc1); - ListCell *lc2; - int indexcol; + Node *clause = (Node *) lfirst(lcc); + int indexcol = lfirst_int(lci); - /* percollists must correspond to index columns */ - Assert(list_length(percollists) <= index->ncolumns); + /* + * 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); - indexcol = 0; - foreach(lc2, percollists) + if (IsA(clause, OpExpr)) { - List *percollist = (List *) lfirst(lc2); - - if (percollist != NIL) - { - Node *clause = (Node *) linitial(percollist); - - /* Should have only one clause per index column */ - Assert(list_length(percollist) == 1); - - /* - * 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); - - if (IsA(clause, OpExpr)) - { - OpExpr *op = (OpExpr *) clause; - - if (list_length(op->args) != 2) - elog(ERROR, "indexorderby clause is not binary opclause"); - - /* - * Now replace the indexkey expression with an index Var. - */ - linitial(op->args) = fix_indexqual_operand(linitial(op->args), - index, - indexcol); - } - else - elog(ERROR, "unsupported indexorderby type: %d", - (int) nodeTag(clause)); + OpExpr *op = (OpExpr *) clause; - fixed_indexorderbys = lappend(fixed_indexorderbys, clause); - } + if (list_length(op->args) != 2) + elog(ERROR, "indexorderby clause is not binary opclause"); - indexcol++; + /* + * Now replace the indexkey expression with an index Var. + */ + linitial(op->args) = fix_indexqual_operand(linitial(op->args), + index, + indexcol); } + else + elog(ERROR, "unsupported indexorderby type: %d", + (int) nodeTag(clause)); + + fixed_indexorderbys = lappend(fixed_indexorderbys, clause); } return fixed_indexorderbys; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5c18b72344d..c5378eb60af 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3296,7 +3296,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, 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 9e99c9d9d7b..5b7be94de5e 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -410,10 +410,14 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel) * Creates a path node for an index scan. * * 'index' is a usable index. - * 'clause_groups' is a list of lists of RestrictInfo nodes + * 'indexclauses' is a list of RestrictInfo nodes representing clauses * to be used as index qual conditions in the scan. - * 'indexorderbys' is a list of lists of lists of bare expressions (not - * RestrictInfos) to be used as index ordering operators. + * 'indexclausecols' is an integer list of index column numbers (zero based) + * the indexclauses can be used with. + * '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) + * the ordering operators can be used with. * 'pathkeys' describes the ordering of the path. * 'indexscandir' is ForwardScanDirection or BackwardScanDirection * for an ordered index, or NoMovementScanDirection for @@ -427,8 +431,10 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel) IndexPath * create_index_path(PlannerInfo *root, IndexOptInfo *index, - List *clause_groups, + List *indexclauses, + List *indexclausecols, List *indexorderbys, + List *indexorderbycols, List *pathkeys, ScanDirection indexscandir, bool indexonly, @@ -437,7 +443,7 @@ create_index_path(PlannerInfo *root, IndexPath *pathnode = makeNode(IndexPath); RelOptInfo *rel = index->rel; List *indexquals, - *allclauses; + *indexqualcols; /* * For a join inner scan, there's no point in marking the path with any @@ -457,16 +463,16 @@ create_index_path(PlannerInfo *root, pathnode->path.pathkeys = pathkeys; /* Convert clauses to indexquals the executor can handle */ - indexquals = expand_indexqual_conditions(index, clause_groups); - - /* Flatten the clause-groups list to produce indexclauses list */ - allclauses = flatten_clausegroups_list(clause_groups); + expand_indexqual_conditions(index, indexclauses, indexclausecols, + &indexquals, &indexqualcols); /* Fill in the pathnode */ pathnode->indexinfo = index; - pathnode->indexclauses = allclauses; + pathnode->indexclauses = indexclauses; pathnode->indexquals = indexquals; + pathnode->indexqualcols = indexqualcols; pathnode->indexorderbys = indexorderbys; + pathnode->indexorderbycols = indexorderbycols; pathnode->isjoininner = (outer_rel != NULL); pathnode->indexscandir = indexscandir; @@ -476,7 +482,7 @@ create_index_path(PlannerInfo *root, /* * We must compute the estimated number of output rows for the * indexscan. This is less than rel->rows because of the additional - * selectivity of the join clauses. Since clause_groups may contain + * selectivity of the join clauses. Since indexclauses may contain * both restriction and join clauses, we have to do a set union to get * the full set of clauses that must be considered to compute the * correct selectivity. (Without the union operation, we might have @@ -489,7 +495,9 @@ create_index_path(PlannerInfo *root, * Note that we force the clauses to be treated as non-join clauses * during selectivity estimation. */ - allclauses = list_union_ptr(rel->baserestrictinfo, allclauses); + List *allclauses; + + allclauses = list_union_ptr(rel->baserestrictinfo, indexclauses); pathnode->rows = rel->tuples * clauselist_selectivity(root, allclauses, @@ -508,8 +516,7 @@ create_index_path(PlannerInfo *root, pathnode->rows = rel->rows; } - cost_index(pathnode, root, index, indexquals, indexorderbys, - indexonly, outer_rel); + cost_index(pathnode, root, outer_rel); return pathnode; } |