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.c228
1 files changed, 165 insertions, 63 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index fa6749a2f9c..26d7c1c2331 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1148,7 +1148,8 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *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().)
+ * index key. (This is depended on by expand_indexqual_conditions() and
+ * fix_indexqual_references().)
*
* 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
@@ -1171,8 +1172,11 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
* column C, and no clauses use column B.
*
* Note: in some circumstances we may find the same RestrictInfos coming
- * from multiple places. Defend against redundant outputs by using
- * list_append_unique_ptr (pointer equality should be good enough).
+ * 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
+ * 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,
@@ -1182,6 +1186,7 @@ group_clauses_by_indexkey(IndexOptInfo *index,
bool *found_clause)
{
List *clausegroup_list = NIL;
+ List *used_clauses = NIL;
bool found_outer_clause = false;
int indexcol;
@@ -1201,13 +1206,16 @@ group_clauses_by_indexkey(IndexOptInfo *index,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Assert(IsA(rinfo, RestrictInfo));
+ if (list_member_ptr(used_clauses, rinfo))
+ continue;
if (match_clause_to_indexcol(index,
indexcol,
rinfo,
outer_relids,
saop_control))
{
- clausegroup = list_append_unique_ptr(clausegroup, rinfo);
+ clausegroup = lappend(clausegroup, rinfo);
+ used_clauses = lappend(used_clauses, rinfo);
if (saop_control != SAOP_REQUIRE ||
IsA(rinfo->clause, ScalarArrayOpExpr))
*found_clause = true;
@@ -1220,13 +1228,16 @@ group_clauses_by_indexkey(IndexOptInfo *index,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Assert(IsA(rinfo, RestrictInfo));
+ if (list_member_ptr(used_clauses, rinfo))
+ continue;
if (match_clause_to_indexcol(index,
indexcol,
rinfo,
outer_relids,
saop_control))
{
- clausegroup = list_append_unique_ptr(clausegroup, rinfo);
+ clausegroup = lappend(clausegroup, rinfo);
+ used_clauses = lappend(used_clauses, rinfo);
found_outer_clause = true;
}
}
@@ -1240,6 +1251,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
clausegroup_list = lappend(clausegroup_list, clausegroup);
}
+ list_free(used_clauses);
+
if (!*found_clause && !found_outer_clause)
return NIL; /* no indexable clauses anywhere */
@@ -1293,7 +1306,7 @@ group_clauses_by_indexkey(IndexOptInfo *index,
* target index column. This is sufficient to guarantee that some index
* condition can be constructed from the RowCompareExpr --- whether the
* remaining columns match the index too is considered in
- * expand_indexqual_rowcompare().
+ * adjust_rowcompare_for_index().
*
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
* the clause is of the form "indexkey op ANY (arrayconst)". Since not
@@ -1553,13 +1566,15 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
* Test whether an index can produce output ordered according to the
* given pathkeys using "ordering operators".
*
- * If it can, return a list of suitable ORDER BY expressions, each of the form
- * "indexedcol operator pseudoconstant". If not, return NIL.
+ * 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.)
*/
static List *
match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys)
{
- List *orderbyexprs = NIL;
+ List *orderbylists = NIL;
ListCell *lc1;
/* Only indexes with the amcanorderbyop property are interesting here */
@@ -1606,7 +1621,17 @@ match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys)
pathkey->pk_opfamily);
if (expr)
{
- orderbyexprs = lappend(orderbyexprs, 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);
found = true;
break;
}
@@ -1620,7 +1645,7 @@ match_index_to_pathkeys(IndexOptInfo *index, List *pathkeys)
return NIL;
}
- return orderbyexprs; /* success! */
+ return orderbylists; /* success! */
}
/*
@@ -2434,9 +2459,11 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
* Given a list of lists of RestrictInfos, flatten it to a list
* of RestrictInfos.
*
- * This is used to flatten out the result of group_clauses_by_indexkey()
- * to produce an indexclauses list. The original list structure mustn't
- * be altered, but it's OK to share copies of the underlying 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)
@@ -2449,6 +2476,38 @@ flatten_clausegroups_list(List *clausegroups)
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 ----
@@ -2577,8 +2636,8 @@ match_index_to_operand(Node *operand,
* 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 clauses that the executor can actually handle. For operators
+ * 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"
@@ -2796,22 +2855,20 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation,
/*
* expand_indexqual_conditions
- * Given a list of sublists of RestrictInfo nodes, produce a flat list
+ * 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 list is ordered by index key, and so the output list is too.
- * (The latter is not depended on by any part of the core planner, I believe,
- * but parts of the executor require it, and so do the amcostestimate
- * functions.)
+ * The input clauses are grouped by index key, 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)
{
- List *resultquals = NIL;
+ List *resultgroups = NIL;
ListCell *lc;
int indexcol;
@@ -2827,6 +2884,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
List *clausegroup = (List *) lfirst(lc);
Oid curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol];
+ List *newgroup = NIL;
ListCell *lc2;
foreach(lc2, clausegroup)
@@ -2844,8 +2902,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
index);
if (boolqual)
{
- resultquals = lappend(resultquals,
- make_simple_restrictinfo(boolqual));
+ newgroup = lappend(newgroup,
+ make_simple_restrictinfo(boolqual));
continue;
}
}
@@ -2856,38 +2914,40 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
*/
if (is_opclause(clause))
{
- resultquals = list_concat(resultquals,
- expand_indexqual_opclause(rinfo,
- curFamily,
+ newgroup = list_concat(newgroup,
+ expand_indexqual_opclause(rinfo,
+ curFamily,
curCollation));
}
else if (IsA(clause, ScalarArrayOpExpr))
{
/* no extra work at this time */
- resultquals = lappend(resultquals, rinfo);
+ newgroup = lappend(newgroup, rinfo);
}
else if (IsA(clause, RowCompareExpr))
{
- resultquals = lappend(resultquals,
- expand_indexqual_rowcompare(rinfo,
- index,
- indexcol));
+ newgroup = lappend(newgroup,
+ expand_indexqual_rowcompare(rinfo,
+ index,
+ indexcol));
}
else if (IsA(clause, NullTest))
{
Assert(index->amsearchnulls);
- resultquals = lappend(resultquals,
- make_simple_restrictinfo(clause));
+ newgroup = lappend(newgroup,
+ make_simple_restrictinfo(clause));
}
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
}
+ resultgroups = lappend(resultgroups, newgroup);
+
indexcol++;
}
- return resultquals;
+ return resultgroups;
}
/*
@@ -3054,6 +3114,41 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
* expand_indexqual_rowcompare --- expand a single indexqual condition
* that is a RowCompareExpr
*
+ * This is a thin wrapper around adjust_rowcompare_for_index; we export the
+ * latter so that createplan.c can use it to re-discover which columns of the
+ * index are used by a row comparison indexqual.
+ */
+static RestrictInfo *
+expand_indexqual_rowcompare(RestrictInfo *rinfo,
+ IndexOptInfo *index,
+ int indexcol)
+{
+ RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
+ Expr *newclause;
+ List *indexcolnos;
+ bool var_on_left;
+
+ newclause = adjust_rowcompare_for_index(clause,
+ index,
+ indexcol,
+ &indexcolnos,
+ &var_on_left);
+
+ /*
+ * If we didn't have to change the RowCompareExpr, return the original
+ * RestrictInfo.
+ */
+ if (newclause == (Expr *) clause)
+ return rinfo;
+
+ /* Else we need a new RestrictInfo */
+ return make_simple_restrictinfo(newclause);
+}
+
+/*
+ * adjust_rowcompare_for_index --- expand a single indexqual condition
+ * that is a RowCompareExpr
+ *
* It's already known that the first column of the row comparison matches
* the specified column of the index. We can use additional columns of the
* row comparison as index qualifications, so long as they match the index
@@ -3066,13 +3161,23 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
* even when the original was "<" or ">" --- this is necessary to match all
* the rows that could match the original. (We are essentially building a
* lossy version of the row comparison when we do this.)
+ *
+ * *indexcolnos receives an integer list of the index column numbers (zero
+ * based) used in the resulting expression. The reason we need to return
+ * that is that if the index is selected for use, createplan.c will need to
+ * call this again to extract that list. (This is a bit grotty, but row
+ * comparison indexquals aren't used enough to justify finding someplace to
+ * keep the information in the Path representation.) Since createplan.c
+ * also needs to know which side of the RowCompareExpr is the index side,
+ * we also return *var_on_left_p rather than re-deducing that there.
*/
-static RestrictInfo *
-expand_indexqual_rowcompare(RestrictInfo *rinfo,
+Expr *
+adjust_rowcompare_for_index(RowCompareExpr *clause,
IndexOptInfo *index,
- int indexcol)
+ int indexcol,
+ List **indexcolnos,
+ bool *var_on_left_p)
{
- RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
bool var_on_left;
int op_strategy;
Oid op_lefttype;
@@ -3094,6 +3199,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
Assert(var_on_left ||
match_index_to_operand((Node *) linitial(clause->rargs),
indexcol, index));
+ *var_on_left_p = var_on_left;
+
expr_op = linitial_oid(clause->opnos);
if (!var_on_left)
expr_op = get_commutator(expr_op);
@@ -3101,6 +3208,10 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
&op_strategy,
&op_lefttype,
&op_righttype);
+
+ /* Initialize returned list of which index columns are used */
+ *indexcolnos = list_make1_int(indexcol);
+
/* Build lists of the opfamilies and operator datatypes in case needed */
opfamilies = list_make1_oid(index->opfamily[indexcol]);
lefttypes = list_make1_oid(op_lefttype);
@@ -3147,28 +3258,22 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
break; /* no good, volatile comparison value */
/*
- * The Var side can match any column of the index. If the user does
- * something weird like having multiple identical index columns, we
- * insist the match be on the first such column, to avoid confusing
- * the executor.
+ * The Var side can match any column of the index.
*/
for (i = 0; i < index->ncolumns; i++)
{
- if (match_index_to_operand(varop, i, index))
+ if (match_index_to_operand(varop, i, index) &&
+ get_op_opfamily_strategy(expr_op,
+ index->opfamily[i]) == op_strategy &&
+ IndexCollMatchesExprColl(index->indexcollations[i],
+ lfirst_oid(collids_cell)))
break;
}
if (i >= index->ncolumns)
break; /* no match found */
- /* Now, do we have the right operator for this column? */
- if (get_op_opfamily_strategy(expr_op, index->opfamily[i])
- != op_strategy)
- break;
-
- /* Does collation match? */
- if (!IndexCollMatchesExprColl(index->indexcollations[i],
- lfirst_oid(collids_cell)))
- break;
+ /* Add column number to returned list */
+ *indexcolnos = lappend_int(*indexcolnos, i);
/* Add opfamily and datatypes to lists */
get_op_opfamily_properties(expr_op, index->opfamily[i], false,
@@ -3189,7 +3294,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
/* Return clause as-is if it's all usable as index quals */
if (matching_cols == list_length(clause->opnos))
- return rinfo;
+ return (Expr *) clause;
/*
* We have to generate a subset rowcompare (possibly just one OpExpr). The
@@ -3260,18 +3365,15 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
matching_cols);
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
matching_cols);
- return make_simple_restrictinfo((Expr *) rc);
+ return (Expr *) rc;
}
else
{
- Expr *opexpr;
-
- opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
- copyObject(linitial(clause->largs)),
- copyObject(linitial(clause->rargs)),
- InvalidOid,
- linitial_oid(clause->inputcollids));
- return make_simple_restrictinfo(opexpr);
+ return make_opclause(linitial_oid(new_ops), BOOLOID, false,
+ copyObject(linitial(clause->largs)),
+ copyObject(linitial(clause->rargs)),
+ InvalidOid,
+ linitial_oid(clause->inputcollids));
}
}