aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/indxpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/indxpath.c')
-rw-r--r--src/backend/optimizer/path/indxpath.c399
1 files changed, 174 insertions, 225 deletions
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;
}
/*