aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/nbtree/nbtutils.c2
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/optimizer/path/costsize.c50
-rw-r--r--src/backend/optimizer/path/indxpath.c399
-rw-r--r--src/backend/optimizer/plan/createplan.c303
-rw-r--r--src/backend/optimizer/plan/planner.c2
-rw-r--r--src/backend/optimizer/util/pathnode.c35
-rw-r--r--src/backend/utils/adt/selfuncs.c309
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h10
-rw-r--r--src/include/nodes/relation.h46
-rw-r--r--src/include/optimizer/cost.h5
-rw-r--r--src/include/optimizer/pathnode.h4
-rw-r--r--src/include/optimizer/paths.h7
-rw-r--r--src/include/pg_config_manual.h2
15 files changed, 529 insertions, 649 deletions
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index bb4d07368ff..90e441c3a98 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -616,7 +616,7 @@ _bt_advance_array_keys(IndexScanDesc scan, ScanDirection dir)
*
* The output keys must be sorted by index attribute. Presently we expect
* (but verify) that the input keys are already so sorted --- this is done
- * by group_clauses_by_indexkey() in indxpath.c. Some reordering of the keys
+ * by match_clauses_to_index() in indxpath.c. Some reordering of the keys
* within each attribute may be done as a byproduct of the processing here,
* but no other code depends on that.
*
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index cdc2cab55c6..60931dcb735 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1512,7 +1512,9 @@ _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_BOOL_FIELD(isjoininner);
WRITE_ENUM_FIELD(indexscandir, ScanDirection);
WRITE_FLOAT_FIELD(indextotalcost, "%.2f");
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;
}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 3e6cabf7e74..cfc7dae55b6 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5970,9 +5970,7 @@ string_to_bytea_const(const char *str, size_t str_len)
static void
genericcostestimate(PlannerInfo *root,
- IndexOptInfo *index,
- List *indexQuals,
- List *indexOrderBys,
+ IndexPath *path,
RelOptInfo *outer_rel,
double numIndexTuples,
Cost *indexStartupCost,
@@ -5980,6 +5978,9 @@ genericcostestimate(PlannerInfo *root,
Selectivity *indexSelectivity,
double *indexCorrelation)
{
+ IndexOptInfo *index = path->indexinfo;
+ List *indexQuals = path->indexquals;
+ List *indexOrderBys = path->indexorderbys;
double numIndexPages;
double num_sa_scans;
double num_outer_scans;
@@ -5991,14 +5992,6 @@ genericcostestimate(PlannerInfo *root,
List *selectivityQuals;
ListCell *l;
- /*
- * For our purposes here, it doesn't matter which index columns the
- * individual quals and order-by expressions go with, so flatten the
- * lists for convenience.
- */
- indexQuals = flatten_clausegroups_list(indexQuals);
- indexOrderBys = flatten_indexorderbys_list(indexOrderBys);
-
/*----------
* If the index is partial, AND the index predicate with the explicitly
* given indexquals to produce a more accurate idea of the index
@@ -6030,7 +6023,7 @@ genericcostestimate(PlannerInfo *root,
if (!predicate_implied_by(oneQual, indexQuals))
predExtraQuals = list_concat(predExtraQuals, oneQual);
}
- /* list_concat avoids modifying the indexQuals list */
+ /* list_concat avoids modifying the passed-in indexQuals list */
selectivityQuals = list_concat(predExtraQuals, indexQuals);
}
else
@@ -6240,14 +6233,13 @@ Datum
btcostestimate(PG_FUNCTION_ARGS)
{
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
- List *indexQuals = (List *) PG_GETARG_POINTER(2);
- List *indexOrderBys = (List *) PG_GETARG_POINTER(3);
- RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(4);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(5);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(6);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(7);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(8);
+ IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
+ RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(2);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+ IndexOptInfo *index = path->indexinfo;
Oid relid;
AttrNumber colnum;
VariableStatData vardata;
@@ -6258,7 +6250,8 @@ btcostestimate(PG_FUNCTION_ARGS)
bool found_saop;
bool found_is_null_op;
double num_sa_scans;
- ListCell *lc1;
+ ListCell *lcc,
+ *lci;
/*
* For a btree scan, only leading '=' quals plus inequality quals for the
@@ -6266,8 +6259,9 @@ btcostestimate(PG_FUNCTION_ARGS)
* the "boundary quals" that determine the starting and stopping points of
* the index scan). Additional quals can suppress visits to the heap, so
* it's OK to count them in indexSelectivity, but they should not count
- * for estimating numIndexTuples. So we must examine the given indexQuals
- * to find out which ones count as boundary quals.
+ * for estimating numIndexTuples. So we must examine the given indexquals
+ * to find out which ones count as boundary quals. We rely on the
+ * knowledge that they are given in index column order.
*
* For a RowCompareExpr, we consider only the first column, just as
* rowcomparesel() does.
@@ -6277,119 +6271,113 @@ btcostestimate(PG_FUNCTION_ARGS)
* considered to act the same as it normally does.
*/
indexBoundQuals = NIL;
+ indexcol = 0;
eqQualHere = false;
found_saop = false;
found_is_null_op = false;
num_sa_scans = 1;
-
- /* clausegroups must correspond to index columns */
- Assert(list_length(indexQuals) <= index->ncolumns);
-
- indexcol = 0;
- foreach(lc1, indexQuals)
+ forboth(lcc, path->indexquals, lci, path->indexqualcols)
{
- List *clausegroup = (List *) lfirst(lc1);
- ListCell *lc2;
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
+ Expr *clause;
+ Node *leftop,
+ *rightop;
+ Oid clause_op;
+ int op_strategy;
+ bool is_null_op = false;
- eqQualHere = false;
+ if (indexcol != lfirst_int(lci))
+ {
+ /* Beginning of a new column's quals */
+ if (!eqQualHere)
+ break; /* done if no '=' qual for indexcol */
+ eqQualHere = false;
+ indexcol++;
+ if (indexcol != lfirst_int(lci))
+ break; /* no quals at all for indexcol */
+ }
+
+ Assert(IsA(rinfo, RestrictInfo));
+ clause = rinfo->clause;
- foreach(lc2, clausegroup)
+ if (IsA(clause, OpExpr))
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc2);
- Expr *clause;
- Node *leftop,
- *rightop;
- Oid clause_op;
- int op_strategy;
- bool is_null_op = false;
-
- Assert(IsA(rinfo, RestrictInfo));
- clause = rinfo->clause;
- if (IsA(clause, OpExpr))
- {
- leftop = get_leftop(clause);
- rightop = get_rightop(clause);
- clause_op = ((OpExpr *) clause)->opno;
- }
- else if (IsA(clause, RowCompareExpr))
- {
- RowCompareExpr *rc = (RowCompareExpr *) clause;
+ leftop = get_leftop(clause);
+ rightop = get_rightop(clause);
+ clause_op = ((OpExpr *) clause)->opno;
+ }
+ else if (IsA(clause, RowCompareExpr))
+ {
+ RowCompareExpr *rc = (RowCompareExpr *) clause;
- leftop = (Node *) linitial(rc->largs);
- rightop = (Node *) linitial(rc->rargs);
- clause_op = linitial_oid(rc->opnos);
- }
- else if (IsA(clause, ScalarArrayOpExpr))
- {
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+ leftop = (Node *) linitial(rc->largs);
+ rightop = (Node *) linitial(rc->rargs);
+ clause_op = linitial_oid(rc->opnos);
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
- leftop = (Node *) linitial(saop->args);
- rightop = (Node *) lsecond(saop->args);
- clause_op = saop->opno;
- found_saop = true;
- }
- else if (IsA(clause, NullTest))
- {
- NullTest *nt = (NullTest *) clause;
+ leftop = (Node *) linitial(saop->args);
+ rightop = (Node *) lsecond(saop->args);
+ clause_op = saop->opno;
+ found_saop = true;
+ }
+ else if (IsA(clause, NullTest))
+ {
+ NullTest *nt = (NullTest *) clause;
- leftop = (Node *) nt->arg;
- rightop = NULL;
- clause_op = InvalidOid;
- if (nt->nulltesttype == IS_NULL)
- {
- found_is_null_op = true;
- is_null_op = true;
- }
- }
- else
+ leftop = (Node *) nt->arg;
+ rightop = NULL;
+ clause_op = InvalidOid;
+ if (nt->nulltesttype == IS_NULL)
{
- elog(ERROR, "unsupported indexqual type: %d",
- (int) nodeTag(clause));
- continue; /* keep compiler quiet */
+ found_is_null_op = true;
+ is_null_op = true;
}
+ }
+ else
+ {
+ elog(ERROR, "unsupported indexqual type: %d",
+ (int) nodeTag(clause));
+ continue; /* keep compiler quiet */
+ }
- if (match_index_to_operand(leftop, indexcol, index))
- {
- /* clause_op is correct */
- }
- else
- {
- Assert(match_index_to_operand(rightop, indexcol, index));
- /* Must flip operator to get the opfamily member */
- clause_op = get_commutator(clause_op);
- }
+ if (match_index_to_operand(leftop, indexcol, index))
+ {
+ /* clause_op is correct */
+ }
+ else
+ {
+ Assert(match_index_to_operand(rightop, indexcol, index));
+ /* Must flip operator to get the opfamily member */
+ clause_op = get_commutator(clause_op);
+ }
- /* check for equality operator */
- if (OidIsValid(clause_op))
- {
- op_strategy = get_op_opfamily_strategy(clause_op,
+ /* check for equality operator */
+ if (OidIsValid(clause_op))
+ {
+ op_strategy = get_op_opfamily_strategy(clause_op,
index->opfamily[indexcol]);
- Assert(op_strategy != 0); /* not a member of opfamily?? */
- if (op_strategy == BTEqualStrategyNumber)
- eqQualHere = true;
- }
- else if (is_null_op)
- {
- /* IS NULL is like = for selectivity determination */
+ Assert(op_strategy != 0); /* not a member of opfamily?? */
+ if (op_strategy == BTEqualStrategyNumber)
eqQualHere = true;
- }
- /* count up number of SA scans induced by indexBoundQuals only */
- if (IsA(clause, ScalarArrayOpExpr))
- {
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
- int alength = estimate_array_length(lsecond(saop->args));
-
- if (alength > 1)
- num_sa_scans *= alength;
- }
- indexBoundQuals = lappend(indexBoundQuals, rinfo);
}
+ else if (is_null_op)
+ {
+ /* IS NULL is like = for purposes of selectivity determination */
+ eqQualHere = true;
+ }
+ /* count up number of SA scans induced by indexBoundQuals only */
+ if (IsA(clause, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+ int alength = estimate_array_length(lsecond(saop->args));
- /* Done with this indexcol, continue to next only if it had = qual */
- if (!eqQualHere)
- break;
-
- indexcol++;
+ if (alength > 1)
+ num_sa_scans *= alength;
+ }
+ indexBoundQuals = lappend(indexBoundQuals, rinfo);
}
/*
@@ -6399,7 +6387,7 @@ btcostestimate(PG_FUNCTION_ARGS)
* NullTest invalidates that theory, even though it sets eqQualHere.
*/
if (index->unique &&
- indexcol == index->ncolumns &&
+ indexcol == index->ncolumns - 1 &&
eqQualHere &&
!found_saop &&
!found_is_null_op)
@@ -6422,8 +6410,8 @@ btcostestimate(PG_FUNCTION_ARGS)
numIndexTuples = rint(numIndexTuples / num_sa_scans);
}
- genericcostestimate(root, index, indexQuals, indexOrderBys,
- outer_rel, numIndexTuples,
+ genericcostestimate(root, path, outer_rel,
+ numIndexTuples,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@@ -6538,16 +6526,14 @@ Datum
hashcostestimate(PG_FUNCTION_ARGS)
{
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
- List *indexQuals = (List *) PG_GETARG_POINTER(2);
- List *indexOrderBys = (List *) PG_GETARG_POINTER(3);
- RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(4);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(5);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(6);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(7);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(8);
-
- genericcostestimate(root, index, indexQuals, indexOrderBys, outer_rel, 0.0,
+ IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
+ RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(2);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+ genericcostestimate(root, path, outer_rel, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@@ -6558,16 +6544,14 @@ Datum
gistcostestimate(PG_FUNCTION_ARGS)
{
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
- List *indexQuals = (List *) PG_GETARG_POINTER(2);
- List *indexOrderBys = (List *) PG_GETARG_POINTER(3);
- RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(4);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(5);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(6);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(7);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(8);
-
- genericcostestimate(root, index, indexQuals, indexOrderBys, outer_rel, 0.0,
+ IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
+ RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(2);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+ genericcostestimate(root, path, outer_rel, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@@ -6578,16 +6562,14 @@ Datum
spgcostestimate(PG_FUNCTION_ARGS)
{
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
- List *indexQuals = (List *) PG_GETARG_POINTER(2);
- List *indexOrderBys = (List *) PG_GETARG_POINTER(3);
- RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(4);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(5);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(6);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(7);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(8);
-
- genericcostestimate(root, index, indexQuals, indexOrderBys, outer_rel, 0.0,
+ IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
+ RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(2);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+
+ genericcostestimate(root, path, outer_rel, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@@ -6901,14 +6883,15 @@ Datum
gincostestimate(PG_FUNCTION_ARGS)
{
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(1);
- List *indexQuals = (List *) PG_GETARG_POINTER(2);
- List *indexOrderBys = (List *) PG_GETARG_POINTER(3);
- RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(4);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(5);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(6);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(7);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(8);
+ IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
+ RelOptInfo *outer_rel = (RelOptInfo *) PG_GETARG_POINTER(2);
+ Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
+ Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
+ Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
+ double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+ IndexOptInfo *index = path->indexinfo;
+ List *indexQuals = path->indexquals;
+ List *indexOrderBys = path->indexorderbys;
ListCell *l;
List *selectivityQuals;
double numPages = index->pages,
@@ -6931,14 +6914,6 @@ gincostestimate(PG_FUNCTION_ARGS)
GinStatsData ginStats;
/*
- * For our purposes here, it doesn't matter which index columns the
- * individual quals and order-by expressions go with, so flatten the
- * lists for convenience.
- */
- indexQuals = flatten_clausegroups_list(indexQuals);
- indexOrderBys = flatten_indexorderbys_list(indexOrderBys);
-
- /*
* Obtain statistic information from the meta page
*/
indexRel = index_open(index->indexoid, AccessShareLock);
@@ -6994,7 +6969,7 @@ gincostestimate(PG_FUNCTION_ARGS)
if (!predicate_implied_by(oneQual, indexQuals))
predExtraQuals = list_concat(predExtraQuals, oneQual);
}
- /* list_concat avoids modifying the indexQuals list */
+ /* list_concat avoids modifying the passed-in indexQuals list */
selectivityQuals = list_concat(predExtraQuals, indexQuals);
}
else
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 94b7d897907..b2bf8f0d8c0 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201112231
+#define CATALOG_VERSION_NO 201112241
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 32be6b13747..c893c3a1708 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -548,7 +548,7 @@ DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f t f v 2
DESCR("btree(internal)");
DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
DESCR("btree(internal)");
-DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
+DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
DESCR("btree(internal)");
DATA(insert OID = 2785 ( btoptions PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ btoptions _null_ _null_ _null_ ));
DESCR("btree(internal)");
@@ -662,7 +662,7 @@ DATA(insert OID = 442 ( hashbulkdelete PGNSP PGUID 12 1 0 0 0 f f f t f v 4
DESCR("hash(internal)");
DATA(insert OID = 425 ( hashvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ hashvacuumcleanup _null_ _null_ _null_ ));
DESCR("hash(internal)");
-DATA(insert OID = 438 ( hashcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ hashcostestimate _null_ _null_ _null_ ));
+DATA(insert OID = 438 ( hashcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ hashcostestimate _null_ _null_ _null_ ));
DESCR("hash(internal)");
DATA(insert OID = 2786 ( hashoptions PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ hashoptions _null_ _null_ _null_ ));
DESCR("hash(internal)");
@@ -917,7 +917,7 @@ DATA(insert OID = 776 ( gistbulkdelete PGNSP PGUID 12 1 0 0 0 f f f t f v 4
DESCR("gist(internal)");
DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
DESCR("gist(internal)");
-DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
+DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
DESCR("gist(internal)");
DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ gistoptions _null_ _null_ _null_ ));
DESCR("gist(internal)");
@@ -3870,7 +3870,7 @@ DATA(insert OID = 2739 ( ginbulkdelete PGNSP PGUID 12 1 0 0 0 f f f t f v 4
DESCR("gin(internal)");
DATA(insert OID = 2740 ( ginvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ ginvacuumcleanup _null_ _null_ _null_ ));
DESCR("gin(internal)");
-DATA(insert OID = 2741 ( gincostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gincostestimate _null_ _null_ _null_ ));
+DATA(insert OID = 2741 ( gincostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gincostestimate _null_ _null_ _null_ ));
DESCR("gin(internal)");
DATA(insert OID = 2788 ( ginoptions PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ ginoptions _null_ _null_ _null_ ));
DESCR("gin(internal)");
@@ -4530,7 +4530,7 @@ DATA(insert OID = 4012 ( spgvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f t f v
DESCR("spgist(internal)");
DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
DESCR("spgist(internal)");
-DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
+DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
DESCR("spgist(internal)");
DATA(insert OID = 4014 ( spgoptions PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ spgoptions _null_ _null_ _null_ ));
DESCR("spgist(internal)");
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index b137142f3ea..13018027b61 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -657,27 +657,33 @@ typedef struct Path
*
* 'indexclauses' is a list of index qualification clauses, with implicit
* AND semantics across the list. Each clause is a RestrictInfo node from
- * the query's WHERE or JOIN conditions.
- *
- * 'indexquals' is a list of sub-lists of the actual index qual conditions
- * that can be used with the index. There is one possibly-empty sub-list
- * for each index column (but empty sub-lists for trailing columns can be
- * omitted). The qual conditions are RestrictInfos, and in simple cases
- * are the same RestrictInfos that appear in the flat indexclauses list.
- * But when special indexable operators appear in 'indexclauses', they are
- * replaced by their derived indexscannable conditions in 'indexquals'.
- * Note that an entirely empty indexquals list denotes a full-index scan.
- *
- * 'indexorderbys', if not NIL, is a list of lists of lists of ORDER BY
- * expressions that have been found to be usable as ordering operators for an
- * amcanorderbyop index. These are not RestrictInfos, just bare expressions,
+ * the query's WHERE or JOIN conditions. An empty list implies a full
+ * index scan.
+ *
+ * 'indexquals' has the same structure as 'indexclauses', but it contains
+ * the actual index qual conditions that can be used with the index.
+ * In simple cases this is identical to 'indexclauses', but when special
+ * indexable operators appear in 'indexclauses', they are replaced by the
+ * derived indexscannable conditions in 'indexquals'.
+ *
+ * 'indexqualcols' is an integer list of index column numbers (zero-based)
+ * of the same length as 'indexquals', showing which index column each qual
+ * is meant to be used with. 'indexquals' is required to be ordered by
+ * index column, so 'indexqualcols' must form a nondecreasing sequence.
+ * (The order of multiple quals for the same index column is unspecified.)
+ *
+ * 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
+ * been found to be usable as ordering operators for an amcanorderbyop index.
+ * The list must match the path's pathkeys, ie, one expression per pathkey
+ * in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. Also, unlike the case for
* quals, it's guaranteed that each expression has the index key on the left
- * side of the operator. The top list has one entry per pathkey in the
- * path's pathkeys, and the sub-lists have one sub-sublist per index column.
- * This representation is a bit of overkill, since there will be only one
- * actual expression per pathkey, but it's convenient because each sub-list
- * has the same structure as the indexquals list.
+ * side of the operator.
+ *
+ * 'indexorderbycols' is an integer list of index column numbers (zero-based)
+ * of the same length as 'indexorderbys', showing which index column each
+ * ORDER BY expression is meant to be used with. (There is no restriction
+ * on which index column each ORDER BY can be used with.)
*
* 'isjoininner' is TRUE if the path is a nestloop inner scan (that is,
* some of the index conditions are join rather than restriction clauses).
@@ -711,7 +717,9 @@ typedef struct IndexPath
IndexOptInfo *indexinfo;
List *indexclauses;
List *indexquals;
+ List *indexqualcols;
List *indexorderbys;
+ List *indexorderbycols;
bool isjoininner;
ScanDirection indexscandir;
Cost indextotalcost;
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 125808ae980..6c56439f109 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -67,9 +67,8 @@ extern double clamp_row_est(double nrows);
extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
double index_pages, PlannerInfo *root);
extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
-extern void cost_index(IndexPath *path, PlannerInfo *root, IndexOptInfo *index,
- List *indexQuals, List *indexOrderBys,
- bool indexonly, RelOptInfo *outer_rel);
+extern void cost_index(IndexPath *path, PlannerInfo *root,
+ RelOptInfo *outer_rel);
extern void cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
Path *bitmapqual, RelOptInfo *outer_rel);
extern void cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 38c8c1c9a35..8eec2ac596d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -30,8 +30,10 @@ extern void add_path(RelOptInfo *parent_rel, Path *new_path);
extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel);
extern 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,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b0075d78654..2fa862fb6f3 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -57,11 +57,10 @@ extern bool eclass_matches_any_index(EquivalenceClass *ec,
RelOptInfo *rel);
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
-extern List *expand_indexqual_conditions(IndexOptInfo *index,
- List *clausegroups);
+extern void expand_indexqual_conditions(IndexOptInfo *index,
+ List *indexclauses, List *indexclausecols,
+ List **indexquals_p, List **indexqualcols_p);
extern void check_partial_indexes(PlannerInfo *root, RelOptInfo *rel);
-extern List *flatten_clausegroups_list(List *clausegroups);
-extern List *flatten_indexorderbys_list(List *indexorderbys);
extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
IndexOptInfo *index,
int indexcol,
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 6c8e31269c5..ac434fabbc4 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -22,7 +22,7 @@
/*
* Maximum number of arguments to a function.
*
- * The minimum value is 9 (index cost estimation uses 9-argument functions).
+ * The minimum value is 8 (GIN indexes use 8-argument support functions).
* The maximum possible value is around 600 (limited by index tuple size in
* pg_proc's index; BLCKSZ larger than 8K would allow more). Values larger
* than needed will waste memory and processing time, but do not directly