aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-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
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;
}