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.c337
1 files changed, 321 insertions, 16 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 159960764fb..9ec5911403f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.196 2005/12/06 16:50:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.197 2006/01/25 20:29:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,6 +61,11 @@ static bool match_clause_to_indexcol(IndexOptInfo *index,
SaOpControl saop_control);
static bool is_indexable_operator(Oid expr_op, Oid opclass,
bool indexkey_on_left);
+static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
+ int indexcol,
+ Oid opclass,
+ RowCompareExpr *clause,
+ Relids outer_relids);
static Relids indexable_outerrelids(RelOptInfo *rel);
static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
Relids outer_relids);
@@ -82,7 +87,10 @@ static bool match_special_index_operator(Expr *clause, Oid opclass,
bool indexkey_on_left);
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
IndexOptInfo *index);
-static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
+static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opclass);
+static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
+ IndexOptInfo *index,
+ int indexcol);
static List *prefix_quals(Node *leftop, Oid opclass,
Const *prefix, Pattern_Prefix_Status pstatus);
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
@@ -900,6 +908,14 @@ group_clauses_by_indexkey(IndexOptInfo *index,
* We do not actually do the commuting here, but we check whether a
* suitable commutator operator is available.
*
+ * It is also possible to match RowCompareExpr clauses to indexes (but
+ * currently, only btree indexes handle this). In this routine we will
+ * report a match if the first column of the row comparison matches the
+ * 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().
+ *
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
* the clause is of the form "indexkey op ANY (arrayconst)". Since the
* executor can only handle these in the context of bitmap index scans,
@@ -944,7 +960,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
/*
* Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
- * (which is always binary, by definition).
+ * (which is always binary, by definition). Or it could be a
+ * RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().
*/
if (is_opclause(clause))
{
@@ -972,6 +989,12 @@ match_clause_to_indexcol(IndexOptInfo *index,
expr_op = saop->opno;
plain_op = false;
}
+ else if (clause && IsA(clause, RowCompareExpr))
+ {
+ return match_rowcompare_to_indexcol(index, indexcol, opclass,
+ (RowCompareExpr *) clause,
+ outer_relids);
+ }
else
return false;
@@ -1039,6 +1062,74 @@ is_indexable_operator(Oid expr_op, Oid opclass, bool indexkey_on_left)
return op_in_opclass(expr_op, opclass);
}
+/*
+ * match_rowcompare_to_indexcol()
+ * Handles the RowCompareExpr case for match_clause_to_indexcol(),
+ * which see for comments.
+ */
+static bool
+match_rowcompare_to_indexcol(IndexOptInfo *index,
+ int indexcol,
+ Oid opclass,
+ RowCompareExpr *clause,
+ Relids outer_relids)
+{
+ Node *leftop,
+ *rightop;
+ Oid expr_op;
+
+ /* Forget it if we're not dealing with a btree index */
+ if (index->relam != BTREE_AM_OID)
+ return false;
+
+ /*
+ * We could do the matching on the basis of insisting that the opclass
+ * shown in the RowCompareExpr be the same as the index column's opclass,
+ * but that does not work well for cross-type comparisons (the opclass
+ * could be for the other datatype). Also it would fail to handle indexes
+ * using reverse-sort opclasses. Instead, match if the operator listed in
+ * the RowCompareExpr is the < <= > or >= member of the index opclass
+ * (after commutation, if the indexkey is on the right).
+ */
+ leftop = (Node *) linitial(clause->largs);
+ rightop = (Node *) linitial(clause->rargs);
+ expr_op = linitial_oid(clause->opnos);
+
+ /*
+ * These syntactic tests are the same as in match_clause_to_indexcol()
+ */
+ if (match_index_to_operand(leftop, indexcol, index) &&
+ bms_is_subset(pull_varnos(rightop), outer_relids) &&
+ !contain_volatile_functions(rightop))
+ {
+ /* OK, indexkey is on left */
+ }
+ else if (match_index_to_operand(rightop, indexcol, index) &&
+ bms_is_subset(pull_varnos(leftop), outer_relids) &&
+ !contain_volatile_functions(leftop))
+ {
+ /* indexkey is on right, so commute the operator */
+ expr_op = get_commutator(expr_op);
+ if (expr_op == InvalidOid)
+ return false;
+ }
+ else
+ return false;
+
+ /* We're good if the operator is the right type of opclass member */
+ switch (get_op_opclass_strategy(expr_op, opclass))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ return true;
+ }
+
+ return false;
+}
+
+
/****************************************************************************
* ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ----
****************************************************************************/
@@ -2014,7 +2105,8 @@ match_special_index_operator(Expr *clause, Oid opclass,
* of index qual clauses. Standard qual clauses (those in the index's
* opclass) are passed through unchanged. Boolean clauses and "special"
* index operators are expanded into clauses that the indexscan machinery
- * will know what to do with.
+ * 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,
@@ -2041,13 +2133,14 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
foreach(l, (List *) lfirst(clausegroup_item))
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+ Expr *clause = rinfo->clause;
/* First check for boolean cases */
if (IsBooleanOpclass(curClass))
{
Expr *boolqual;
- boolqual = expand_boolean_index_clause((Node *) rinfo->clause,
+ boolqual = expand_boolean_index_clause((Node *) clause,
indexcol,
index);
if (boolqual)
@@ -2061,16 +2154,31 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
}
}
- /* Next check for ScalarArrayOp cases */
- if (IsA(rinfo->clause, ScalarArrayOpExpr))
+ /*
+ * Else it must be an opclause (usual case), ScalarArrayOp, or
+ * RowCompare
+ */
+ if (is_opclause(clause))
{
+ resultquals = list_concat(resultquals,
+ expand_indexqual_opclause(rinfo,
+ curClass));
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ /* no extra work at this time */
resultquals = lappend(resultquals, rinfo);
- continue;
}
-
- resultquals = list_concat(resultquals,
- expand_indexqual_condition(rinfo,
- curClass));
+ else if (IsA(clause, RowCompareExpr))
+ {
+ resultquals = lappend(resultquals,
+ expand_indexqual_rowcompare(rinfo,
+ index,
+ indexcol));
+ }
+ else
+ elog(ERROR, "unsupported indexqual type: %d",
+ (int) nodeTag(clause));
}
clausegroup_item = lnext(clausegroup_item);
@@ -2145,16 +2253,15 @@ expand_boolean_index_clause(Node *clause,
}
/*
- * expand_indexqual_condition --- expand a single indexqual condition
- * (other than a boolean-qual or ScalarArrayOp case)
+ * expand_indexqual_opclause --- expand a single indexqual condition
+ * that is an operator clause
*
* The input is a single RestrictInfo, the output a list of RestrictInfos
*/
static List *
-expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
+expand_indexqual_opclause(RestrictInfo *rinfo, Oid opclass)
{
Expr *clause = rinfo->clause;
-
/* we know these will succeed */
Node *leftop = get_leftop(clause);
Node *rightop = get_rightop(clause);
@@ -2225,6 +2332,204 @@ expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
}
/*
+ * expand_indexqual_rowcompare --- 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
+ * in the "same direction", ie, the indexkeys are all on the same side of the
+ * clause and the operators are all the same-type members of the opclasses.
+ * If all the columns of the RowCompareExpr match in this way, we just use it
+ * as-is. Otherwise, we build a shortened RowCompareExpr (if more than one
+ * column matches) or a simple OpExpr (if the first-column match is all
+ * there is). In these cases the modified clause is always "<=" or ">="
+ * 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.)
+ */
+static RestrictInfo *
+expand_indexqual_rowcompare(RestrictInfo *rinfo,
+ IndexOptInfo *index,
+ int indexcol)
+{
+ RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
+ bool var_on_left;
+ int op_strategy;
+ Oid op_subtype;
+ bool op_recheck;
+ int matching_cols;
+ Oid expr_op;
+ List *opclasses;
+ List *subtypes;
+ List *new_ops;
+ ListCell *largs_cell;
+ ListCell *rargs_cell;
+ ListCell *opnos_cell;
+
+ /* We have to figure out (again) how the first col matches */
+ var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
+ indexcol, index);
+ Assert(var_on_left ||
+ match_index_to_operand((Node *) linitial(clause->rargs),
+ indexcol, index));
+ expr_op = linitial_oid(clause->opnos);
+ if (!var_on_left)
+ expr_op = get_commutator(expr_op);
+ get_op_opclass_properties(expr_op, index->classlist[indexcol],
+ &op_strategy, &op_subtype, &op_recheck);
+ /* Build lists of the opclasses and operator subtypes in case needed */
+ opclasses = list_make1_oid(index->classlist[indexcol]);
+ subtypes = list_make1_oid(op_subtype);
+
+ /*
+ * See how many of the remaining columns match some index column
+ * in the same way. A note about rel membership tests: we assume
+ * that the clause as a whole is already known to use only Vars from
+ * the indexed relation and possibly some acceptable outer relations.
+ * So the "other" side of any potential index condition is OK as long
+ * as it doesn't use Vars from the indexed relation.
+ */
+ matching_cols = 1;
+ largs_cell = lnext(list_head(clause->largs));
+ rargs_cell = lnext(list_head(clause->rargs));
+ opnos_cell = lnext(list_head(clause->opnos));
+
+ while (largs_cell != NULL)
+ {
+ Node *varop;
+ Node *constop;
+ int i;
+
+ expr_op = lfirst_oid(opnos_cell);
+ if (var_on_left)
+ {
+ varop = (Node *) lfirst(largs_cell);
+ constop = (Node *) lfirst(rargs_cell);
+ }
+ else
+ {
+ varop = (Node *) lfirst(rargs_cell);
+ constop = (Node *) lfirst(largs_cell);
+ /* indexkey is on right, so commute the operator */
+ expr_op = get_commutator(expr_op);
+ if (expr_op == InvalidOid)
+ break; /* operator is not usable */
+ }
+ if (bms_is_member(index->rel->relid, pull_varnos(constop)))
+ break; /* no good, Var on wrong side */
+ if (contain_volatile_functions(constop))
+ 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.
+ */
+ for (i = 0; i < index->ncolumns; i++)
+ {
+ if (match_index_to_operand(varop, i, index))
+ break;
+ }
+ if (i >= index->ncolumns)
+ break; /* no match found */
+
+ /* Now, do we have the right operator for this column? */
+ if (get_op_opclass_strategy(expr_op, index->classlist[i])
+ != op_strategy)
+ break;
+
+ /* Add opclass and subtype to lists */
+ get_op_opclass_properties(expr_op, index->classlist[i],
+ &op_strategy, &op_subtype, &op_recheck);
+ opclasses = lappend_oid(opclasses, index->classlist[i]);
+ subtypes = lappend_oid(subtypes, op_subtype);
+
+ /* This column matches, keep scanning */
+ matching_cols++;
+ largs_cell = lnext(largs_cell);
+ rargs_cell = lnext(rargs_cell);
+ opnos_cell = lnext(opnos_cell);
+ }
+
+ /* Return clause as-is if it's all usable as index quals */
+ if (matching_cols == list_length(clause->opnos))
+ return rinfo;
+
+ /*
+ * We have to generate a subset rowcompare (possibly just one OpExpr).
+ * The painful part of this is changing < to <= or > to >=, so deal with
+ * that first.
+ */
+ if (op_strategy == BTLessEqualStrategyNumber ||
+ op_strategy == BTGreaterEqualStrategyNumber)
+ {
+ /* easy, just use the same operators */
+ new_ops = list_truncate(list_copy(clause->opnos), matching_cols);
+ }
+ else
+ {
+ ListCell *opclasses_cell;
+ ListCell *subtypes_cell;
+
+ if (op_strategy == BTLessStrategyNumber)
+ op_strategy = BTLessEqualStrategyNumber;
+ else if (op_strategy == BTGreaterStrategyNumber)
+ op_strategy = BTGreaterEqualStrategyNumber;
+ else
+ elog(ERROR, "unexpected strategy number %d", op_strategy);
+ new_ops = NIL;
+ forboth(opclasses_cell, opclasses, subtypes_cell, subtypes)
+ {
+ expr_op = get_opclass_member(lfirst_oid(opclasses_cell),
+ lfirst_oid(subtypes_cell),
+ op_strategy);
+ if (!OidIsValid(expr_op)) /* should not happen */
+ elog(ERROR, "could not find member %d of opclass %u",
+ op_strategy, lfirst_oid(opclasses_cell));
+ if (!var_on_left)
+ {
+ expr_op = get_commutator(expr_op);
+ if (!OidIsValid(expr_op)) /* should not happen */
+ elog(ERROR, "could not find commutator of member %d of opclass %u",
+ op_strategy, lfirst_oid(opclasses_cell));
+ }
+ new_ops = lappend_oid(new_ops, expr_op);
+ }
+ }
+
+ /* If we have more than one matching col, create a subset rowcompare */
+ if (matching_cols > 1)
+ {
+ RowCompareExpr *rc = makeNode(RowCompareExpr);
+
+ if (var_on_left)
+ rc->rctype = (RowCompareType) op_strategy;
+ else
+ rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ?
+ ROWCOMPARE_GE : ROWCOMPARE_LE;
+ rc->opnos = new_ops;
+ rc->opclasses = list_truncate(list_copy(clause->opclasses),
+ matching_cols);
+ rc->largs = list_truncate((List *) copyObject(clause->largs),
+ matching_cols);
+ rc->rargs = list_truncate((List *) copyObject(clause->rargs),
+ matching_cols);
+ return make_restrictinfo((Expr *) rc, true, false, NULL);
+ }
+ else
+ {
+ Expr *opexpr;
+
+ opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
+ copyObject(linitial(clause->largs)),
+ copyObject(linitial(clause->rargs)));
+ return make_restrictinfo(opexpr, true, false, NULL);
+ }
+}
+
+/*
* Given a fixed prefix that all the "leftop" values must have,
* generate suitable indexqual condition(s). opclass is the index
* operator class; we use it to deduce the appropriate comparison